首先求出最小生成树,我们枚举每条不在最小生成树上的边,并把这条边放到最小生成树上面,然后就一定会形成环,那么我们在这条环路中取出一条最长的路(除了新加入的那一条边)。最终我们得到的权值就是次小生成树的权值。
prim算法实现:
我们在求解次小生成树的时候我们要使用一个二维数组maxd[i][j]表示最小生成树中i点到j点的最远距离,我们使用动态规划的思想来计算这个数组,比如当前节点为x,他的父亲节点为per[x],以及根节点root,那么 maxd[root][x] = max(maxd[root][per[x]] , maxd[per[x]][x]);
Kruskal算法实现:
kruskla算法中我们枚举的边权值会依次增大,那么就会给我们计算提供一定的便利,但是因为kruskal的实现方式和prim有所不同,所以kruskal需要存储当前最小生成树中的节点,然后我们再去更新maxd数组。
prim:
1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 #include <cstdio> 5 #define INF 0x3f3f3f3f 6 7 using namespace std; 8 const int MAXN = 110; 9 int n,m,mapp[MAXN][MAXN]; 10 bool vis[MAXN],connect[MAXN][MAXN]; 11 int dis[MAXN],maxd[MAXN][MAXN],per[MAXN]; 12 void Init() 13 { 14 memset(mapp,INF,sizeof(mapp)); 15 memset(connect,false,sizeof(connect)); 16 } 17 int prim() 18 { 19 memset(maxd,0,sizeof(maxd)); 20 memset(vis,false,sizeof(vis)); 21 for(int i = 1;i <= n;i ++) 22 { 23 dis[i] = mapp[1][i];per[i] = 1;//首先父亲节点都是根节点 24 } 25 vis[1] = 1; 26 dis[1] = 0; 27 int res = 0; 28 for(int i = 1;i < n;i ++) 29 { 30 int index = -1,temp = INF; 31 for(int j = 1;j <= n;j ++) 32 if(!vis[j] && dis[j] < temp) 33 { 34 index = j;temp = dis[j]; 35 } 36 if(index == -1) return res; 37 vis[index] = 1; 38 connect[index][per[index]] = false;connect[per[index]][index] = false;//这条边已经在最小生成树中,后面我们就不能添加这条边了 39 res += temp; 40 maxd[per[index]][index] =maxd[index][per[index]] = temp;//更新点之间的最大值 41 for(int j = 1;j <= n;j ++) 42 { 43 if(j != index && vis[j])//只是更新我们已经遍历过来的节点 44 { 45 maxd[index][j] = maxd[j][index] = max(maxd[j][per[index]],dis[index]); 46 } 47 if(!vis[j] && mapp[index][j] < dis[j]) 48 { 49 dis[j] = mapp[index][j]; 50 per[j] = index; 51 } 52 } 53 } 54 return res; 55 } 56 int main() 57 { 58 int T; 59 scanf("%d ",&T); 60 while(T--) 61 { 62 scanf("%d%d",&n,&m); 63 Init(); 64 int u,v,w; 65 for(int i = 0;i < m;i ++) 66 { 67 scanf("%d%d%d",&u,&v,&w); 68 mapp[u][v] = w;mapp[v][u] = w; 69 connect[u][v] = true;connect[v][u] = true; 70 } 71 int ans = prim(); 72 bool over = false; 73 //如果有某条边没有被最小生成树使用,并且i~j的最大值大于图中得到最大值,那么就表示次小生成树存在 74 //相反就不会存在 75 for(int i = 1;!over && i <= n;i ++) 76 for(int j = 1;j <= n;j ++) 77 { 78 if(connect[i][j] == false || mapp[i][j] == INF) 79 continue; 80 if(mapp[i][j] == maxd[i][j])//当边长度相同是就是表示最小生成树相同 81 { 82 over = 1; 83 break; 84 } 85 } 86 if(over) 87 printf("Not Unique! "); 88 else 89 printf("%d ",ans); 90 } 91 //如果我们需要求解次小生成树的权值时,我们就要把在最小生成树中没有用过的边,加上然后减去对应环中最大的路径 92 return 0; 93 }
kruskal:
1 #include <vector> 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #define INF 0x3f3f3f3f 7 8 using namespace std; 9 int n,m; 10 struct data 11 { 12 int u,v,w; 13 bool vis; 14 } p[20010]; 15 vector<int>G[110]; 16 int per[110],maxd[110][110]; 17 bool cmp(data a,data b) 18 { 19 return a.w < b.w; 20 } 21 int Union_Find(int x) 22 { 23 return x == per[x] ? x: per[x] = Union_Find(per[x]); 24 } 25 void kruskal() 26 { 27 sort(p,p+m,cmp); 28 for(int i=0; i<=n; i++)//初始化 29 { 30 G[i].clear(); 31 G[i].push_back(i); 32 per[i]=i; 33 } 34 int sum=0,k=0;//sum是最小生成树的值 35 for(int i=0; i<m; i++) 36 { 37 if(k==n-1) break; 38 int x1=Union_Find(p[i].u),x2=Union_Find(p[i].v); 39 if(x1!=x2) 40 { 41 k++; 42 p[i].vis=1;//这条边已经用过了 43 sum+=p[i].w; 44 int len_x1=G[x1].size(); 45 int len_x2=G[x2].size(); 46 for(int j=0; j<len_x1; j++)//更新两点之间距离的最大值 47 for(int k=0; k<len_x2; k++) 48 maxd[G[x1][j]][G[x2][k]]=maxd[G[x2][k]][G[x1][j]]=p[i].w;//因为后面的边会越来越大,所以这里可以直接等于当前边的长度 49 per[x1]=x2; 50 //因为per[x1] = x2,在Union_Find函数中要寻找和x1相关联节点的跟节点的时候,都会找到x2,所以这里不用再去更新和x1节点相连的节点 51 //十分感谢Self-Discipline博主,提出此问题 52 // int tem[110]; 53 // for(int j=0; j<len_x2; j++)//现在已经属于一棵树了,那么我们就将点添加到相应的集合中 54 // tem[j]=G[x2][j]; 55 for(int j=0; j<len_x1; j++) 56 G[x2].push_back(G[x1][j]); 57 // for(int j=0; j<len_x2; j++) 58 // G[x1].push_back(tem[j]); 59 } 60 } 61 int cisum=INF;//次小生成树的权值 62 for(int i=0; i<m; i++) 63 if(!p[i].vis) 64 cisum=min(cisum,sum+p[i].w-maxd[p[i].u][p[i].v]); 65 if(cisum>sum) 66 printf("%d ",sum); 67 else 68 printf("Not Unique! "); 69 } 70 int main() 71 { 72 int T; 73 scanf("%d ",&T); 74 while(T--) 75 { 76 scanf("%d%d",&n,&m); 77 for(int i=0; i<m; i++) 78 { 79 scanf("%d%d%d",&p[i].u,&p[i].v,&p[i].w); 80 p[i].vis = false; 81 } 82 kruskal(); 83 } 84 return 0; 85 }