最小生成树-Prim算法和Kruskal算法
Posted lewin671
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最小生成树-Prim算法和Kruskal算法相关的知识,希望对你有一定的参考价值。
在解决这个问题之前,我觉得有必要先解释一下什么叫做生成树,什么叫做最小生成树。给定一个图,如果它的某个子图中任意两个顶点都互相联通并且是一棵树,那么这棵树就叫做生成树。如果边上有权值,那么使得权值和最小的树叫做最小生成树。
安全边:当一条边(u,v)加入T时,必须保证T∪{(u,v)}仍是MST的子集,我们将这样的边称为T的安全边。
求MST(minimum spanning tree)的一般算法可描述为:针对图G,从空树T开始,往集合T中逐条选择并加入n-1条安全边(u,v),最终生成一棵含n-1条边的MST。求解最小生成树的方法主要有以下两种:
一、Prim算法:
贪心的将MST和其他顶点相连的最小权值的边加入MST,重复操作,直到MST中有n条边。如果我们要高效地使用Prim算法,我们可以使用最小堆。下面看一下这个算法的图解:
二、kruskal算法
kuskal的贪心策略更加容易理解:每次都选取最小权值的安全边,然后加入MST,直到MST中有n-1条边。当然,高效的查找需要用到并查集,比如下面的第二道例题,而且也需要使用最小堆。下面看kruskal的图解:
三、经典题目:
1.HDU 1233
还是畅通工程
描述
Input
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
当N为0时,输入结束,该用例不被处理。
Output
对每个测试用例,在1行里输出最小的公路总长度。
Sample Input
3 1 2 1 1 3 2 2 3 4 4 1 2 1 1 3 4 1 4 1 2 3 3 2 4 2 3 4 5 0
Sample Output
3 5
#include<iostream> #include<cstdio> using namespace std; const int maxn=105; const int INF=0x3fffffff; int graph[maxn][maxn]; int n; int Prim() { int min_cost[maxn]; bool used[maxn]; for(int i=0;i<maxn;i++) { min_cost[i]=INF; used[i]=false; } min_cost[1]=0; int res=0; while(1) { int v=-1; for(int u=1;u<=n;u++) { if(!used[u]&&(v==-1||min_cost[u]<min_cost[v])) v=u; } if(v==-1) break; used[v]=true; res+=min_cost[v]; for(int u=1;u<=n;u++) { min_cost[u]=min(min_cost[u],graph[v][u]); } } return res; } int main() { while(~scanf("%d",&n)&&n) { for(int i=0;i<=n;i++) for(int j=0;j<maxn;j++) graph[i][j]=INF; int vertex1,vertex2,weight; for(int i=1;i<=n*(n-1)/2;i++) { scanf("%d%d%d",&vertex1,&vertex2,&weight); graph[vertex1][vertex2]=weight; graph[vertex2][vertex1]=weight; } printf("%d\\n",Prim()); } }
2.Constructing Roads (HDU 1102)
We know that there are already some roads between some villages and your job is the build some roads such that all the villages are connect and the length of all the roads built is minimum.
InputThe first line is an integer N (3 <= N <= 100), which is the number of villages. Then come N lines, the i-th of which contains N integers, and the j-th of these N integers is the distance (the distance should be an integer within [1, 1000]) between village i and village j.
Then there is an integer Q (0 <= Q <= N * (N + 1) / 2). Then come Q lines, each line contains two integers a and b (1 <= a < b <= N), which means the road between village a and village b has been built.
OutputYou should output a line contains an integer, which is the length of all the roads to be built such that all the villages are connected, and this value is minimum.
Sample Input
3 0 990 692 990 0 179 692 179 0 1 1 2
Sample Output
179
#include<set> #include<cstdio> using namespace std; struct Node { int x,y,cost; Node() {} Node(int xx,int yy,int cc):x(xx),y(yy),cost(cc){} friend bool operator<(Node a,Node b); }; const int maxn=10000; int fa[maxn]; void init() { for(int i=0;i<maxn;i++) fa[i]=i; } int GetRoot(int x) { if(x!=fa[x]) { return fa[x]=GetRoot(fa[x]); } return fa[x]; } void Unite(int x,int y) { int fx=GetRoot(x); int fy=GetRoot(y); fa[fx]=fy; } int main() { int n; multiset<Node> s; while(~scanf("%d",&n)) { init(); s.clear(); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { int num; scanf("%d",&num); if(i!=j) { s.insert(Node(i,j,num)); } } } int q,ans=0,counter=0; //counter表示已经连接的边数 scanf("%d",&q); while(q--) { int xx,yy; scanf("%d%d",&xx,&yy); if(GetRoot(xx)!=GetRoot(yy)) counter++; Unite(xx,yy); } multiset<Node>::iterator it=s.begin(); while(counter<n-1) { Node node=(*it); int rx=GetRoot(node.x); int ry=GetRoot(node.y); if(rx!=ry) { Unite(rx,ry); ans+=it->cost; counter++; } it++; } printf("%d\\n",ans); } } bool operator<(Node a,Node b) { return a.cost<b.cost; }
以上是关于最小生成树-Prim算法和Kruskal算法的主要内容,如果未能解决你的问题,请参考以下文章