【次小生成树】【Kruskal】【prim】【转】
原博客出处:https://blog.csdn.net/yasola/article/details/74276255
通常次小生成树是使用Prim算法进行实现的,因为可以在Prim算法松弛的同时求得最小生成树上任意两点之间的最长边。但是利用Kruskal算法却没办法在松弛的同时求得。
所以我们就要在Kruskal求完最短路后,对于每个顶点bfs一次,得到树上任意两点的最长边。之后求可以像之前一样枚举不在树上的边,代替找最小值了。
两种方法的时间杂度是一样的,但Kruskal的实现代码回长非常多,不过Kruskal的实现可以处理Prim难以处理的重边。
一、Kruskal模板
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #include <cmath> 6 #include <vector> 7 #include <queue> 8 using namespace std; 9 #define INF 0x3f3f3f3f 10 #define fi first 11 #define se second 12 #define mem(a,b) memset((a),(b),sizeof(a)) 13 14 const int MAXV=100+3; 15 const int MAXE=200+3; 16 17 struct Edge 18 { 19 int from,to,cost; 20 Edge(int f=0,int t=0,int c=0):from(f),to(t),cost(c){} 21 bool operator<(const Edge &other)const 22 { 23 return cost<other.cost; 24 } 25 }edge[MAXE]; 26 27 int V,E,par[MAXV],high[MAXV],the_max[MAXV][MAXV]; 28 bool used[MAXE];//边是否使用的标记 29 bool vis[MAXV]; 30 vector<pair<int,int> > G[MAXV];//最小生成树 31 32 void init()//初始化 33 { 34 for(int i=1;i<=E;++i) 35 used[i]=false; 36 for(int i=1;i<=V;++i) 37 { 38 par[i]=i; 39 high[i]=0; 40 G[i].clear(); 41 } 42 } 43 44 int findfather(int x) 45 { 46 return par[x]=par[x]==x?x:findfather(par[x]); 47 } 48 49 bool unite(int a,int b) 50 { 51 int fa=findfather(a),fb=findfather(b); 52 if(fa==fb) 53 return false; 54 if(high[fa]>high[fb]) 55 par[fb]=fa; 56 else 57 { 58 par[fa]=fb; 59 if(high[fa]==high[fb]) 60 ++high[fb]; 61 } 62 return true; 63 } 64 65 void bfs(int s) 66 { 67 mem(vis,0); 68 vis[s]=true; 69 the_max[s][s]=0; 70 queue<int> que; 71 que.push(s); 72 while(!que.empty()) 73 { 74 int u=que.front(); que.pop(); 75 for(int i=0;i<G[u].size();++i) 76 { 77 int v=G[u][i].fi; 78 if(!vis[v]) 79 { 80 vis[v]=true; 81 the_max[s][v]=max(the_max[s][u],G[u][i].se); 82 the_max[v][s]=the_max[s][v]; 83 que.push(v); 84 } 85 } 86 } 87 } 88 89 int main() 90 { 91 int T_T; 92 scanf("%d",&T_T); 93 for(int cas=1;cas<=T_T;++cas) 94 { 95 printf("Case #%d : ",cas); 96 scanf("%d%d",&V,&E); 97 init(); 98 for(int i=0;i<E;++i) 99 scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].cost); 100 sort(edge,edge+E); 101 int res=0; 102 for(int i=0;i<E;++i) 103 if(unite(edge[i].from,edge[i].to)) 104 { 105 res+=edge[i].cost; 106 used[i]=true; 107 G[edge[i].from].push_back(make_pair(edge[i].to,edge[i].cost)); 108 G[edge[i].to].push_back(make_pair(edge[i].from,edge[i].cost)); 109 } 110 bool ok=true; 111 for(int i=2;i<=V;++i) 112 if(findfather(i)!=findfather(1)) 113 { 114 ok=false; 115 break; 116 } 117 if(!ok)//不联通 118 { 119 puts("No way"); 120 continue; 121 } 122 if(E==V-1)//生成树唯一 123 { 124 puts("No second way"); 125 continue; 126 } 127 for(int i=1;i<=V;++i) 128 bfs(i); 129 int ans=INF; 130 for(int i=0;i<E;++i) 131 if(!used[i]) 132 ans=min(ans,res-the_max[edge[i].from][edge[i].to]+edge[i].cost); 133 printf("%d\n",ans); 134 } 135 136 return 0; 137 }
二、prim模板
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int inf=0x3f3f3f3f; 4 const int maxn=100+10; 5 bool link[maxn][maxn],vis[maxn]; 6 int w[maxn][maxn],lowc[maxn],pre[maxn],Max[maxn][maxn]; 7 int n,m; 8 int prim() 9 { 10 int i,j,p,k; 11 int minc,res=0; 12 memset(vis,false,sizeof(vis)); 13 memset(pre,0,sizeof(pre)); 14 memset(Max,0,sizeof(Max)); 15 vis[1]=true,pre[1]=1; 16 for(i=2; i<=n; i++) //初始化 17 { 18 lowc[i]=w[1][i]; 19 pre[i]=1; 20 } 21 for(i=2; i<=n; i++) //prim 22 { 23 minc=inf,p=-1; 24 for(j=1; j<=n; j++) 25 { 26 if(!vis[j]&&lowc[j]<minc) 27 { 28 minc=lowc[j]; 29 p=j; 30 } 31 } 32 vis[p]=true; 33 res+=minc;//最小生成树加权值 34 Max[pre[p]][p]=minc;//直接相连的两点最大权值就是边权值本身 35 link[pre[p]][p]=true;//将这两条边标记为最小生成树的边 36 link[p][pre[p]]=true; 37 for(k=1; k<=n; k++) 38 Max[k][p]=max(Max[pre[p]][p],Max[k][p]);//非直接相连的最大权值需要不断更新 39 for(j=1; j<=n; j++) 40 if(!vis[j]&&lowc[j]>w[p][j]) 41 { 42 lowc[j]=w[p][j]; 43 pre[j]=p; 44 } 45 46 } 47 return res; 48 } 49 int main() 50 { 51 // freopen("in.txt","r",stdin); 52 // freopen("out.txt","w",stdout); 53 int s,e,t,ans,ans1; 54 55 while(~scanf("%d%d",&n,&m)) 56 { 57 int i,j; 58 bool ok=true;//是否唯一最小生成树的标志 59 for(i=1; i<=n; i++) 60 for(j=1; j<=n; j++) 61 w[i][j]=inf; 62 memset(link,false,sizeof(link)); 63 for(i=1; i<=m; i++) 64 { 65 scanf("%d%d%d",&s,&e,&t); 66 w[s][e]=t; 67 w[e][s]=t; 68 } 69 ans=prim();//最小生成树的权值 70 for(i=1; i<=n; i++) 71 { 72 for(j=i+1; j<=n; j++) 73 { 74 if(w[i][j]!=inf&&!link[i][j]) 75 { 76 ans1=ans+w[i][j]-Max[i][j];//ans1次小生成树的权值 77 } 78 if(ans1==ans) 79 { 80 ok=0; 81 break; 82 } 83 } 84 if(!ok) 85 break; 86 } 87 printf("ans=%d ans1=%d\n",ans,ans1); 88 } 89 return 0; 90 }