次小生成树(POJ1679/CDOJ1959)
Posted demian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了次小生成树(POJ1679/CDOJ1959)相关的知识,希望对你有一定的参考价值。
POJ1679
首先求出最小生成树,记录权值之和为MinST。然后枚举添加边(u,v),加上后必形成一个环,找到环上非(u,v)边的权值最大的边,把它删除,计算当前生成树的权值之和,取所有枚举加边后生成树权值之和的最小值
思路:
最小生成树唯一性判断,求次小生成树的val再与最小生成树比较。
1.先用prim算法求出最小生成树。并在其过程中保存加入到MST中的Max[i][j]( i 到 j 路径中的最大边 )
2.对未加入MST的边进行遍历:从MST中去掉i j路径中的最大边,加入未访问边G[i][j],如果得到的生成树的权值和MST的相等,则存在次小生成树的权值=MST的权值和
1 #include <algorithm> 2 #include <iostream> 3 #include <cstdio> 4 #include <cmath> 5 #include <cstring> 6 using namespace std; 7 #define INF 0x3f3f3f3f 8 9 const int N = 1e3+6; 10 int G[N][N]; // graph matrix 11 int f[N]; // pre node 12 int vis[N]; // visited node 13 int dis[N]; // main matrix: the edges(set-u) 14 int mark[N][N]; 15 int Max[N][N]; // path from i to j max edge 16 int n,m,best; 17 18 int prim(int v) 19 { 20 int ans=0; 21 vis[v]=1; 22 for(int i=1;i<=n;i++) 23 { 24 dis[i]=G[v][i]; 25 f[i]=v; 26 } 27 dis[v]=0; 28 for(int i=1;i<n;i++) // n-1 29 { 30 int u=-1; 31 for(int j=1;j<=n;j++) 32 if(!vis[j]&&(u==-1||dis[j]<dis[u]))u=j; 33 if(u==-1)break; 34 vis[u]=1; 35 mark[u][f[u]] = mark[f[u]][u] = 1; 36 ans += dis[u]; 37 for(int j=1;j<=n;j++) 38 { 39 // compare with visited node record path max edge 40 if(vis[j])Max[u][j]=Max[j][u]=max(Max[j][f[u]],dis[u]); 41 if(!vis[j]&&dis[j]>G[u][j]) 42 { 43 dis[j]=G[u][j]; 44 f[j]=u; 45 } 46 } 47 } 48 return ans; 49 } 50 51 int SMST() 52 { 53 int mini=INF; 54 for(int i=1;i<=n;i++) 55 for(int j=1;j<=n;j++) //or for(int j=i+1;j<=n;j++) 56 { 57 if(i!=j&&!mark[i][j]) 58 mini = min(mini,best+G[i][j]-Max[i][j]); 59 } 60 return mini; 61 } 62 63 int main() 64 { 65 int x,y,w,T; 66 scanf("%d",&T); 67 while(T--) 68 { 69 memset( vis, 0 ,sizeof(vis) ); 70 memset( Max, 0 ,sizeof(Max) ); 71 memset( f, 0 ,sizeof(f) ); 72 memset( dis, 0 ,sizeof(dis) ); 73 memset( mark, 0 ,sizeof(mark) ); 74 scanf("%d%d",&n,&m); 75 for(int i=1;i<=n;i++) 76 for(int j=1;j<=n;j++) 77 { 78 if(i==j)G[i][j]==0; 79 else G[i][j]=INF; 80 } 81 for(int i=0;i<m;i++) 82 { 83 scanf("%d%d%d",&x,&y,&w); 84 G[x][y]=w; 85 G[y][x]=w; 86 } 87 best = prim(1); 88 int temp = SMST(); 89 if(temp == best) 90 printf("Not Unique! "); 91 else 92 printf("%d ",best); 93 } 94 return 0; 95 }
CDOJ 1959
思路:这道题会出现重边,用Kruskal算法处理起来比较方便。(用邻接表方便,邻接矩阵还要考虑同两点之间的多条权值相同的边。)
次小生成树的权值如果等于最小生成树的权值, 其替换边只会是权值相同的边,于是可以把权值相同边放在一起考虑,分别判断全部没有合并时有多少可以合并(cnt1记录的是可以加入集合的边数),边合并边判断有多少可以合并(cnt2记录的是选中一条加入到集合),如果可选的大于构成最小生成树所需要的,那么就存在一种相同的边权可以替换原来的,从而最小生成树不唯一。
1 #include <bits/stdc++.h> 2 #define LL long long 3 using namespace std; 4 const int N = 2e5+6; 5 struct Node 6 { 7 int u,v; 8 LL w; 9 }G[N]; 10 11 int n,m; 12 int f[2002]; 13 int find(int x) 14 { 15 return x==f[x]?x:f[x]=find(f[x]); 16 } 17 18 bool cmp(Node & a, Node & b) 19 { 20 return a.w<b.w; 21 } 22 23 void kruskal() 24 { 25 int cnt1=0; 26 int cnt2=0; 27 for(int i=1;i<=n;i++)f[i]=i; 28 sort(G,G+m,cmp); 29 for(int i=0;i<m;) 30 { 31 int j=i; 32 while(j<m&&G[j].w==G[i].w) 33 { 34 int u = find(G[j].u); 35 int v = find(G[j].v); 36 if(u!=v)cnt1++; 37 j++; 38 } 39 j=i; 40 while(j<m&&G[j].w==G[i].w) 41 { 42 int u = find(G[j].u); 43 int v = find(G[j].v); 44 if(u!=v){ 45 cnt2++; 46 f[u]=v; 47 } 48 j++; 49 } 50 i=j; 51 if(cnt1>cnt2)break; 52 } 53 if( cnt1 > cnt2 ){ 54 printf("zin "); 55 }else{ 56 printf("ogisosetsuna "); 57 } 58 } 59 60 int main() 61 { 62 int x,y; 63 LL w; 64 cin>>n>>m; 65 for(int i=0;i<m;i++) 66 { 67 cin>>x>>y>>w; 68 G[i].u=x; 69 G[i].v=y; 70 G[i].w=w; 71 } 72 kruskal(); 73 return 0; 74 }
以上是关于次小生成树(POJ1679/CDOJ1959)的主要内容,如果未能解决你的问题,请参考以下文章
POJ 1679 The Unique MST(次小生成树)
POJ - 1679 The Unique MST (次小生成树)