处理何种问题:求解最小生成树,适合点多边少的无向图。(以证明,放心用)
性能:时间复杂度为O(e*loge),e为边的个数。
原理:贪心策略
实现步骤:
<1>设一个有n个顶点的联通网络为G(V,E),最初先构造一个只有n个顶点,没有边的非连通图T={V,空},图中的每一个顶点自成一个连通分量。
<2>在E中选择一条具有最小权值的边时,若该边的两个顶点落在不同的连通分量上,则将此边加入到T中;否则,即这条边的两个顶点属于同一个连通分量,将此边舍去(此后永不选用这条边),重新选择一条权值最小的边。
<3>如此重复下去,直至选了(n-1)条有效边,或者所有的顶点都在同一连通分量上为止。
备注:在此之前,本人对于网上给出的对于克鲁斯卡尔的证明并不理解,只觉得这种贪心策略有Bug,这次换一个角度去理解这个算法:<1>一个孤立的点V要想连入最后的那个生成树中,只需要找到在边的中找连接V,且权值最小的那条边即可,则可证明,权值最小的那条边必然在最后的最小生成树中;<2>对于已经连线的两个孤立点,可看作一个点,他们共享互相边的性质。<3>一颗生成树有且仅有n-1条边。根据以上三点,即可推出克鲁斯卡尔的正确性。只是在实现方式上有些难以理解。
输入样例解释:
4 6//顶点的个数,边的个数
1 2 1//一条边的两个顶点和该边上的权值
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
输出样例解释:
5//该无向图的最小生成树
代码
#include<iostream> #include<cstdio> #include<string.h> #include<algorithm> using namespace std; const int MaxN=510;///最大顶点的数 const int MaxM=251000;///最大边数 int F[MaxN];///并查集 struct Edge { int u,v,w; }; Edge edge[MaxM];///储存边的信信息,包括起点/终点/权值 int tal;///边数,加边前赋值为0 void addedge(int u,int v,int w) { edge[tal].u=u; edge[tal].v=v; edge[tal].w=w; ++tal; } bool cmp(Edge a,Edge b)///排序函数,边按照权值从小到大排序 { return a.w<b.w; } int Find(int x) { if(F[x]==-1) return x; else return F[x]=Find(F[x]); } int Kruskal(int n)///传入点数,返回最小生成树的权值,如果不连通则返回-1 { memset(F,-1,sizeof(F)); sort(edge,edge+tal,cmp); int cnt=0;///计算加入的边数 int ans=0; int u,v,w,t1,t2; for(int i=0;i<tal;++i) { u=edge[i].u; v=edge[i].v; w=edge[i].w; t1=Find(u); t2=Find(v); if(t1!=t2) { ans+=w; F[t1]=t2; cnt++; } if(cnt==n-1) break; } if(cnt<n-1) return -1;///不连通 else return ans; } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { tal=0;///必须是0 int u,v,w; for(int i=0;i<m;++i) { scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); } printf("%d ",Kruskal(n)); } return 0; } /** 4 6 1 2 1 1 3 4 1 4 1 2 3 3 2 4 2 3 4 5 5 */