http://poj.org/problem?id=1679
Description
Definition 1 (Spanning Tree): Consider a connected, undirected graph G = (V, E). A spanning tree of G is a subgraph of G, say T = (V', E'), with the following properties:
1. V' = V.
2. T is connected and acyclic.
Definition 2 (Minimum Spanning Tree): Consider an edge-weighted, connected, undirected graph G = (V, E). The minimum spanning tree T = (V, E') of G is the spanning tree that has the smallest total cost. The total cost of T means the sum of the weights on all the edges in E'.
Input
Output
Sample Input
2 3 3 1 2 1 2 3 2 3 1 3 4 4 1 2 2 2 3 2 3 4 2 4 1 2
Sample Output
3 Not Unique!
题意:
给n点m边无重边,求最小生成树是否唯一,如果这棵最小生成树是唯一的那么就输出最小生成树上权的和,不是唯一的就输出Not Unique!
思路:
求次小生成树,如果和最小生成树结果一样则不唯一。
次小生成树:
次小生成树由最小生成树变化而来,通过最小生成树概念可以知道“次小”只需要通过变化最小生成树上的一条边实现,并且要使得这种变化是最小。
给出一篇写的挺好的博客:https://blog.csdn.net/qq_27437781/article/details/70821413
from:https://blog.csdn.net/u011721440/article/details/38735547
判断最小生成树是否唯一:
1、对图中每条边,扫描其它边,如果存在相同权值的边,则标记该边。
2、用kruskal或prim求出MST。
3、如果MST中无标记的边,则MST唯一;否则,在MST中依次去掉标记的边,再求MST,若求得MST权值和原来的MST权值相同,则MST不唯一。
from:https://blog.csdn.net/blue_skyrim/article/details/51338375
次小生成树的求法是枚举最小生成树的每条边,把其中一条边去掉,找到这两点上其他的边,剩下的边形成最小生成树
kuangbin大佬的博客:https://www.cnblogs.com/kuangbin/p/3147329.html
思路:
求最小生成树时,用数组maxval[i][j]来表示MST中i到j最大边权,求完后,直接枚举所有不在MST中的边,替换掉最大边权的边,更新答案 ,注意点的编号从0开始
原理:
最小生成树上的不相邻的两点相连必定成成为一个环,所以我们可以尝试枚举这些不相邻的点使他们相连,再删除环中属于最小生成树的最大边(令当前被确定的点为u,已经被确定的点为v,则u--v路径中最大的边要么来自v--pre[u]路径中的最大,要么就是当前被确定的边lowval[u],dp的思想),这样既保证树的结构又能使树的变化最小。这些枚举中最小的结果即为次小生成树。
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <string> 5 #include <math.h> 6 #include <algorithm> 7 #include <vector> 8 #include <stack> 9 #include <queue> 10 #include <set> 11 #include <map> 12 #include <sstream> 13 const int INF=0x3f3f3f3f; 14 typedef long long LL; 15 const int mod=1e9+7; 16 //const double PI=acos(-1); 17 #define Bug cout<<"---------------------"<<endl 18 const int maxn=110; 19 using namespace std; 20 21 int G[maxn][maxn];//邻接矩阵 22 int vis[maxn];//判断点有没在最小生成树中 23 int pre[maxn];//每个点的双亲 24 int lowval[maxn];//辅助数组 25 int maxval[maxn][maxn];//maxval[i][j]表示在最小生成树中从i到j的路径中的最大边权 26 int used[maxn][maxn];//判断这条边是否在最小生成树中使用过 27 int MST;//最小生成树权值和 28 29 int Prim(int n,int st)//n为顶点的个数,st为最小生成树的开始顶点 30 { 31 fill(lowval,lowval+n,INF); 32 memset(maxval,0,sizeof(maxval)); 33 memset(pre,-1,sizeof(pre)); 34 memset(used,0,sizeof(used)); 35 memset(vis,0,sizeof(vis)); 36 int ans=0; 37 lowval[st]=0; 38 vis[st]=1; 39 for(int i=0;i<n;i++) 40 { 41 if(i!=st&&G[st][i]!=INF) 42 { 43 lowval[i]=min(lowval[i],G[st][i]); 44 pre[i]=st; 45 } 46 } 47 for(int k=0;k<n-1;k++) 48 { 49 int MIN=INF; 50 int t=-1; 51 for(int i=0;i<n;i++) 52 { 53 if(vis[i]==0&&lowval[i]<MIN) 54 { 55 MIN=lowval[i]; 56 t=i; 57 } 58 } 59 // if(MIN==INF) return -1; 60 ans+=MIN; 61 vis[t]=1; 62 used[t][pre[t]]=used[pre[t]][t]=1;//标记这条边在最小生成树中 63 for(int i=0;i<n;i++) 64 { 65 if(vis[i]) 66 maxval[t][i]=maxval[i][t]=max(maxval[i][pre[t]],lowval[t]); 67 if(i!=t&&!vis[i]&&G[t][i]<lowval[i]) 68 { 69 pre[i]=t; 70 lowval[i]=G[t][i]; 71 } 72 } 73 } 74 return ans; 75 } 76 77 int Judge(int n) 78 { 79 int MIN=INF; 80 for(int i=0;i<n;i++) 81 { 82 for(int j=i+1;j<n;j++) 83 { 84 if(G[i][j]!=INF && !used[i][j])//边不在最小生成树中 85 MIN=min(MIN,MST-maxval[i][j]+G[i][j]); 86 } 87 } 88 return MIN; 89 } 90 91 int main() 92 { 93 int T; 94 scanf("%d",&T); 95 while(T--) 96 { 97 int n,m; 98 scanf("%d %d",&n,&m); 99 memset(G,INF,sizeof(G)); 100 for(int i=0;i<m;i++) 101 { 102 int u,v,w; 103 scanf("%d %d %d",&u,&v,&w); 104 u--;v--;//使标号从0开始 105 G[u][v]=w; 106 G[v][u]=w; 107 } 108 MST=Prim(n,0); 109 if(MST==Judge(n))//最小生成树和次小生成树总权值相等 110 printf("Not Unique! "); 111 else 112 printf("%d ",MST); 113 } 114 return 0; 115 }