最小生成树模板&&总结--Prim&&Kruskal
一.最小生成树:连通N个点的边权值总和最小的树。
二.时间复杂度
Prim算法:时间复杂度O(|V|2+|E|),O(|E|log|V|)
Kruskal算法:时间复杂度O(|E|log|E|)
算法的选择: 从图的稀疏程度考虑(稠密图Prim,稀疏图Kruskal或Prim + Heap)
三.具体算法
1.Prim算法:(1) 任意选定一点s,设集合S={s}
(2) 从不在集合S的点中选出一个点j使得其与S内的某点i的距离最短,则(i,j)就是生成树上的一条边,同时将j点加入S
(3) 转到(2)继续进行,直至所有点都己加入S集合
Prim模板:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <map> #include <set> #include <stack> #include <vector> #define Twhile() int T;scanf("%d",&T);while(T--) #define ArrInit(a,b,n) for(int i=0;i<=n;i++)a[i]=b #define ArrInit2(a,b,n,m) for(int i=0;i<=n;i++)for(int j=0;j<=m;j++)a[i][j]=b #define fora(i,a,b) for(int i=a;i<b;i++) #define fors(i,a,b) for(int i=a;i>b;i--) #define fora2(i,a,b) for(int i=a;i<=b;i++) #define fors2(i,a,b) for(int i=a;i>=b;i--) #define PI acos(-1.0) #define eps 1e-6 #define INF 0x3f3f3f3f typedef long long LL; typedef long long LD; using namespace std; const int maxn=2000+11; int ma[maxn][maxn]; int N,M;//点数,边数 int ans;//最小生成树的权值之和 //stack<int>S; //最小生成树加入的点的顺序 //int lowerPoint[maxn]; // i离S中的lowerPoint[i]最近 int lowerCost[maxn],use[maxn];//lowerCost离S的最小距离,use是否被加入S void init() { ans=0; ArrInit2(ma,INF,N,N); ArrInit(use,0,N); } void primInit() { // S.push(1); use[1]=1; lowerCost[1]=0; // lowerPoint[1]=1; fora2(i,2,N) { // lowerPoint[i]=1; lowerCost[i]=ma[1][i]; } } void prim() { primInit(); int n=N-1; while(n--){ int k=-1,tem=INF; fora2(i,2,N)//找到距离S集合最小的点 { if(use[i])continue; if(lowerCost[i]<tem) { tem=lowerCost[i]; k=i; } } use[k]=1; ans+=tem; /* lowerCost[k]=0;lowerPoint[i]=i; //不更新的话,当要求输出最小生成树的边可以直接输出 S.push(k); */ fora2(i,1,N) { if(use[i])continue; int t=ma[i][k]; if(t<lowerCost[i]) { lowerCost[i]=t; //lowerPoint[i]=k; } } if(tem==INF)return;//图不连通 } } int main() { while(~scanf("%d",&N)&&N) { scanf("%d",&M); init(); while(M--) { int x,y,z; scanf("%d%d%d",&x,&y,&z); ma[x][y]=min(ma[x][y],z); ma[y][x]=min(ma[y][x],z); } if(N==1){printf("0\n");continue;} prim(); printf("%d\n",ans); } return 0; }
2.Kruskal算法:(1) 将边按权值从小到大排序后逐个判断,如果当前的边加入以后不会产生环,那么就把当前边作为生成树的一条边
(2) 最终得到的结果就是最小生成树
Kruskal模板:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <string> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <map> #include <set> #include <stack> #include <vector> #define Twhile() int T;scanf("%d",&T);while(T--) #define ArrInit(a,b,n) for(int i=0;i<=n;i++)a[i]=b #define ArrInit2(a,b,n,m) for(int i=0;i<=n;i++)for(int j=0;j<=m;j++)a[i][j]=b #define fora(i,a,b) for(int i=a;i<b;i++) #define fors(i,a,b) for(int i=a;i>b;i--) #define fora2(i,a,b) for(int i=a;i<=b;i++) #define fors2(i,a,b) for(int i=a;i>=b;i--) #define PI acos(-1.0) #define eps 1e-6 #define INF 0x3f3f3f3f typedef long long LL; typedef long long LD; using namespace std; const int maxn=5000+11; int N,M,ans;//点,边,最小生成树 struct edge { int u,v;//点u到v int var;//权值 }ma[maxn]; bool cmp(edge a,edge b) { return a.var<b.var; } int fa[maxn]; void initKruskal() { ans=0; fora2(i,1,N) { fa[i]=i; } sort(ma+1,ma+M+1,cmp); } /* Kruskal算法 (1) 将边按权值从小到大排序后逐个判断,如果当前的边加入以后不会产生环,那么就把当前边作为生成树的一条边 (2) 最终得到的结果就是最小生成树 并查集 */ int findx(int x) { if(x==fa[x])return x; return fa[x]=findx(fa[x]); } bool unio(int x,int y) { int fx=findx(x),fy=findx(y); if(fx==fy)return false; fa[fy]=fx; return true; } void kruskal() { initKruskal(); int m=0; fora2(i,1,M) { if(unio(ma[i].u,ma[i].v)) { m++; ans+=ma[i].var; //printf("%d %d %d\n",ma[i].u,ma[i].v,m); } if(m==N-1)return; } }