【问题标题】:Bidirectional spanning tree双向生成树
【发布时间】:2012-05-04 05:55:24
【问题描述】:

我从 interviewstreet.com 遇到了这个问题

机器再次袭击了锡安王国。王国 Xions 拥有 N 个城市和 N-1 条双向道路。路网是 这样任何一对城市之间都有一条独特的路径。

Morpheus 得知 K Machines 计划摧毁 整个王国。这些机器最初生活在 K 不同 王国的城市,从现在起他们可以随时计划和启动 攻击。所以他要求尼奥破坏一些道路来扰乱 机器之间的连接,即在摧毁那里的那些道路之后 不应该是任何两台机器之间的任何路径。

既然攻击可以在以后的任何时间进行,Neo 不得不做这个任务 尽可能快。王国中的每条道路都需要一定的时间 被摧毁,它们一次只能被摧毁一个。

你需要编写一个程序告诉 Neo 最少的时间 他将要求中断机器之间的连接。

示例输入输入的第一行包含两个,空格分隔 整数 N 和 K。城市编号为 0 到 N-1。然后按照 N-1 行,每行包含三个以空格分隔的整数 x y z,其中 表示有一条双向道路连接城市 x 和城市 y,并且 摧毁这条路需要 z 个单位的时间。然后按照K线 每个都包含一个整数。第i个整数是第i个城市的id 机器当前位于。

输出格式在一行中打印所需的最短时间 中断机器之间的连接。

示例输入

5 3
2 1 8
1 0 5
2 4 5
1 3 4
2
4
0

样本输出

10

解释 Neo 可以摧毁连接城市 2 和城市 4 的道路 权重为 5 ,连接城市 0 和城市 1 的道路权重为 5。如 一次只能摧毁一条道路,所需的总最短时间 是 10 个时间单位。在摧毁这些道路后,没有任何机器 可以通过任何路径到达其他机器。

约束

2 <= N <= 100,000
2 <= K <= N
1 <= time to destroy a road <= 1000,000

有人能告诉我如何解决这个问题吗?

【问题讨论】:

  • 这里提示:如果有N顶点和N-1边,并且图仍然是连通的(没有“岛”),那么图是一条直线.
  • 您对我的回答的评论是正确的 - 上述条件并不意味着直线图。我暂时删除了我的答案。

标签: algorithm graph spanning-tree


【解决方案1】:

所有三个答案都将导致正确的解决方案,但您无法在 interviewstreet.com 提供的时限内得到解决方案。你必须想一些简单的方法来成功地解决这个问题。

提示:从机器所在的节点开始。

【讨论】:

    【解决方案2】:

    王国有 N 个城市,N-1 条边并且是全连接的,因此我们的王国是tree(在图论中)。在这张图片中,您可以看到输入图的树形表示,其中机器由红色顶点表示。

    顺便说一下,您应该考虑从根顶点到所有叶节点的所有路径。因此,在每条路径中,您都会有几个红色节点,并且在删除边缘期间,您应该只考虑相邻的红色节点。例如,在路径 0-10 中有两个有意义的对 - (0,3) 和 (3,10)。并且您必须从成对连接顶点的每条路径中准确删除一个节点(不少于,不少于)。

    我希望这个建议会有所帮助。

    【讨论】:

    • 你的图片和样本输入有什么关系?样本有 5 个城市(和 3 台机器),你的树要大得多。
    • 我并不打算让这张图片与示例输入相对应。这只是一个说明,以便更好地理解我的建议。
    【解决方案3】:

    正如其他人所说,具有N个顶点和N-1条边的连通图是一棵树。

    这类问题需要一个贪婪的解决方案;我会去修改Kruskal's algorithm:

    从一组 N 个组件开始 - 每个节点(城市)1 个。跟踪哪些组件包含机器占用的城市。

    一次取 1 条边(道路),按权重降序排列(从破坏成本最高的道路开始)。对于这条边(它必然连接两个组件 - 图形是一棵树):

    • 如果两个相邻组件都包含机器占领的城市,则必须摧毁这条道路,并标记为这样
    • 否则,将相邻组件合并为一个。如果其中一个包含机器占领的城市,那么合并的组件也是如此。

    完成所有边后,返回被毁道路的成本总和。

    复杂性将与 Kruskal 算法相同,即对于选择良好的数据结构和排序方法而言几乎是线性的。

    【讨论】:

      【解决方案4】:

      pjotr 有一个正确答案(虽然不是渐近最优),但是这个陈述

      这类问题需要贪心解法

      确实需要证明,因为在现实世界中(区别于竞争性编程),有几个这种“类型”的问题,贪婪解决方案不是最优的(例如,这非常一般图中的问题,称为多端割,是 NP 难的)。在这种情况下,证明包括验证matroid 公理。设一组边 A &subseteq;如果图 (V, E &setminus; A) 恰好有 |A|,则 E 独立 + 1 个包含至少一台机器的连接组件。

      空集的独立性。微不足道。

      遗传属性。令 A 为独立集。每条边 e &in; A 连接图的两个连通分量 (V, E &setminus; A),每个连通分量至少包含一台机器。将 e 放回图中,包含至少一台机器的连通分量的数量减少了 1,因此 A &setminus; {e} 也是独立的。

      增广性质。 设 A 和 B 是具有 |A| 的独立集

      【讨论】:

      • 同意。有了一些经验,人们就会对应该使用什么方法来解决这些简单的问题有所了解(在这里,我们称之为“查看方法”),但证明总是更好。
      【解决方案5】:

      从任一机器节点开始执行 DFS。此外,跟踪到目前为止遇到的最小权重的边缘。一旦你找到下一个也包含机器的节点,删除到目前为止记录的最小边。现在从这个新节点启动 DFS。 重复直到找到机器所在的所有节点。

      应该是 O(N) 那样!

      【讨论】:

        【解决方案6】:

        我写了一些代码,并粘贴了所有的测试。

        #include <iostream>
        #include<algorithm>
        using namespace std;
        
        class Line {
        public:
            Line(){
                begin=0;end=0;  weight=0;
        }
        int begin;int end;int weight;
        
        bool operator<(const Line& _l)const {
            return weight>_l.weight;
        }
        };
        
        class Point{
        public:
        Point(){
            pre=0;machine=false;
        }
        int pre;
        bool machine;
        };
        
        void DP_Matrix();
        void outputLines(Line* lines,Point* points,int N);
        
        int main() {
            DP_Matrix();
            system("pause");
            return 0;
        }   
        
        int FMSFind(Point* trees,int x){
            int r=x;
            while(trees[r].pre!=r)
                r=trees[r].pre;
            int i=x;int j;
            while(i!=r) {
                    j=trees[i].pre;
                trees[i].pre=r;
                i=j;
            }
        return r;
        }
        
        void DP_Matrix(){
        int N,K,machine_index;scanf("%d%d",&N,&K);
        Line* lines=new Line[100000];
        Point* points=new Point[100000];
        N--;
        for(int i=0;i<N;i++) {
            scanf("%d%d%d",&lines[i].begin,&lines[i].end,&lines[i].weight);
            points[i].pre=i;
        }
        points[N].pre=N;
        for(int i=0;i<K;i++) {
            scanf("%d",&machine_index);
            points[machine_index].machine=true;
        }
        long long finalRes=0;
        for(int i=0;i<N;i++) {
            int bP=FMSFind(points,lines[i].begin);
            int eP=FMSFind(points,lines[i].end);
            if(points[bP].machine&&points[eP].machine){
                finalRes+=lines[i].weight;
            }
            else{
                points[bP].pre=eP;
                points[eP].machine=points[bP].machine||points[eP].machine;
                points[bP].machine=points[eP].machine;
            }
        }
        cout<<finalRes<<endl;
        delete[] lines;
        delete[] points;
        }
        
        void outputLines(Line* lines,Point* points,int N){
        printf("\nLines:\n");
        for(int i=0;i<N;i++){
            printf("%d\t%d\t%d\n",lines[i].begin,lines[i].end,lines[i].weight);
        }
        printf("\nPoints:\n");
        for(int i=0;i<=N;i++){
            printf("%d\t%d\t%d\n",i,points[i].machine,points[i].pre);
        }
        }
        

        【讨论】:

        • 我认为引导提问者并帮助他们自己解决问题会更好,而不是仅仅粘贴代码。
        猜你喜欢
        • 2011-02-17
        • 1970-01-01
        • 1970-01-01
        • 2014-06-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-10
        • 2016-10-16
        相关资源
        最近更新 更多