双向生成树

时间:2012-05-04 05:55:25

标签: algorithm graph spanning-tree

我在interviewstreet.com上看到了这个问题

  

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

     Morpheus有消息说K Machines正计划摧毁它   整个王国。这些机器最初生活在K不同的地方   王国的城市,从现在起他们可以随时计划和发射   攻击。所以他要求Neo摧毁一些破坏的道路   机器之间的连接,即在那里摧毁那些道路之后   不应该是任何两台机器之间的任何路径。

     

由于攻击可以从现在开始,Neo必须完成这项任务   尽可能快地。王国的每条道路都需要一定的时间   被摧毁,他们只能一次摧毁一个。

     

您需要编写一个程序,告诉Neo最短的时间   他将要求破坏机器之间的联系。

     

示例输入输入的第一行包含两个空格分隔的行   整数,N和K.城市编号为0到N-1。然后按照N-1   每行包含三个空格分隔的整数x y z   意味着有一条连接城市x和城市y的双向道路,和   摧毁这条路需要z个单位的时间。然后按照K行   每个都包含一个整数。 Ith integer是第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

有人可以说明如何处理解决方案。

6 个答案:

答案 0 :(得分:2)

Tree

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

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

我希望这个建议很有帮助。

答案 1 :(得分:2)

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

这种问题需要贪婪的解决方案;我要修改Kruskal's algorithm

  

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

     

一次取1条边(道路),按重量减少排序(从破坏成本最高的道路开始)。对于此边缘(必须连接两个组件 - 图形是树):

     
      
  • 如果两个neigboring组件都包含机器占用的城市,则必须销毁此道路,并将其标记为
  •   
  • 否则,将neigboring组件合并为一个。如果其中一个包含机器占用的城市,合并的组件也是如此。
  •   
     

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

复杂性与Kruskal算法相同,即对于精心挑选的数据结构和排序方法几乎是线性的。

答案 2 :(得分:2)

pjotr有一个正确的答案(虽然不是非常渐近最优)但是这句话

  

这类问题需要贪婪的解决方案

确实需要证据,就像在现实世界中一样(与竞争性编程区分开来),贪婪解决方案不是最佳的“种类”有几个问题(例如,这一点一般图中的问题,称为多端切割并且是NP难的)。在这种情况下,证据包括验证matroid公理。如果图形(V,E∖A)具有正确的| A |,则让一组边A A E 独立。 + 1个连接组件,至少包含一台机器。

空集的独立性。琐碎。

遗传财产。让A成为一个独立的集合。每个边e∈A连接图的两个连通分量(V,E∖A),每个连通分量至少包含一台机器。将e放回图表中,包含至少一台机器的连接组件数量减少1,因此A∖{e}也是独立的。

增强属性。让A和B成为| A |的独立集合&LT; | B |。由于(V,E∖B)具有比(V,E∖A)更多的连通分量,因此存在一对机器u,v,使得u和v由B而不是A断开。因为那里正好是从u到v的一条路径,B在此路径上至少包含一条边e,A不能包含e。删除A∪{e}会导致另一个连接组件包含至少一台机器而不是A,因此A∪{e}是独立的,视需要而定。

答案 3 :(得分:2)

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

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

答案 4 :(得分:0)

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

应该是O(N)那样!!

答案 5 :(得分:-5)

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

#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);
}
}