题目: https://vjudge.net/problem/POJ-2485
最小生成树算法(Prim算法讲得好): http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html
这篇Kruskal算法讲得更清楚 : http://blog.csdn.net/luomingjun12315/article/details/47700237
1、Prim算法
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
//prim解 #include<stdio.h> #include<set> using namespace std; const int qwq = 65536+10; int size ; const int n = 500+10; set<int>v;//初始结点集合 set<int>::iterator it; int rs; void build(int map[n][n]) { const int start = 0; while(!v.empty()) { it=v.begin(); int k =*it; int min = qwq; //找距离v最近的结点 while(it!= v.end()) { if(min>map[start][*it]) { min=map[start][*it]; k=*it; } it++; } // printf("最短的:%d->长度%d\n",k,min);1 if(rs<min) { rs=min; } int newstart = k; for(int i =0;i<size;i++) { if(map[start][i]>map[k][i]) { map[start][i]=map[k][i]; } } v.erase(k); } } int main() { int t; scanf("%d",&t); while(t--) { scanf("%d",&size); int map[n][n]; // { // {0,7,qwq,5,qwq,qwq,qwq}, // {7,0,8,9,7,qwq,qwq}, // {qwq,8,0,qwq,5,qwq,qwq}, // {5,9,qwq,0,15,6,qwq}, // {qwq,7,5,15,0,8,9}, // {qwq,qwq,qwq,6,8,0,11}, // {qwq,qwq,qwq,qwq,9,11,0} // }; for(int i=0;i<size;i++) { for(int j=0;j<size;j++) { scanf("%d",&map[i][j]); } } rs=-1; for(int i=1;i<size;i++) { v.insert(i); } build(map); printf("%d\n",rs); } return 0; }
2、Kruskal算法描述
Kruskal算法是基于贪心的思想得到的。首先我们把所有的边按照权值先从小到大排列,接着按照顺序选取每条边,如果这条边的两个端点不属于同一集合,那么就将它们合并,直到所有的点都属于同一个集合为止。至于怎么合并到一个集合,那么这里我们就可以用到一个工具——-并查集(不知道的同学请移步:Here)。换而言之,Kruskal算法就是基于并查集的贪心算法。
//kruskal 把边按权值从小到大排序,从小到大 遍历每一条边, 如果边的两个端点不再同一个联通分支中,就连接起来 #include<stdio.h> #include<algorithm> #include<memory.h> using namespace std; struct Node { int from; int to; int wei; }; //int cmp(Node a,Node b) //{ // return a.wei<b.wei; //} int cmp(const void *a,const void *b) { Node aa = *(Node*)a; Node bb = *(Node*)b; return aa.wei-bb.wei; } //-----------并查集部分 ----------- int pre[500+10]; int find(int x) { int r= x; while(pre[r]!=r) { r=pre[r]; } int i=x,j; while(i!=r) { j= pre[i]; pre[i]=r; i=j; } return r; } void join(int a,int b) { int fa=find(a); int fb=find(b); if(fa!=fb) { pre[fa]=fb; } } //------------------ const int size = (500+1)*500/2; int s; int main() { int t; scanf("%d",&t); while(t--) { memset(pre,0,sizeof(pre)); s=0; Node edge[size]; int n; scanf("%d",&n); for(int i=0;i<n;i++) { pre[i]=i; } for(int i=0;i<n;i++) { for(int j=0;j<n;j++) { int a; scanf("%d",&a); if(i<j) //输入每一条边的信息 { edge[s].from=i; edge[s].to=j; edge[s].wei=a; s++; } } } qsort(edge,s,sizeof(edge[0]),cmp); // sort(edge,edge+s,cmp); int rs = -1; int t=0; for(int i=0;i<s;i++) { int from = edge[i].from; int to = edge[i].to; if(find(from)!=find(to))//不在同一个集合,合并 { join(from,to); if(rs<edge[i].wei) { rs=edge[i].wei; } t++;//连接的边数 } if(t==n-1){//全部的点已经连在一起了 break; } } printf("%d\n",rs); } return 0; }