The Unique MST
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 20293 | Accepted: 7124 |
Description
Given a connected undirected graph, tell if its minimum spanning tree is unique.
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
The first line contains a single integer t (1 <= t <= 20), the number of test cases. Each case represents a graph. It begins with a line containing two integers n and m (1 <= n <= 100), the number of nodes and edges. Each of the following m lines contains a
triple (xi, yi, wi), indicating that xi and yi are connected by an edge with weight = wi. For any two nodes, there is at most one edge connecting them.
Output
For each input, if the MST is unique, print the total cost of it, or otherwise print the string ‘Not Unique!‘.
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!
Source
次小生成树问题,看了我好久,感觉还是差那么一点点啊,看了好多大神的博客,自己重写了一下,终于过了,思路还不是很清晰啊,prim算法中运用的dp的思想,对dp还是不怎么懂啊,下阶段要好好看看dp,krusal的次小生成树还没怎么看懂,还是要多做题;
参考了博客http://blog.csdn.net/jarily/article/details/8912538
直接把其中的算法介绍部分转过来啦,还是要多看几遍
- 算法引入:
- *设G=(V,E,w)是连通的无向图,T是图G的一棵最小生成树;
- *如果有另一棵树T1,满足不存在树T’,ω(T’)<ω(T1),则称T1是图G的次小生成树;
- *
- *算法思想:
- *邻集的概念:由T进行一次可行交换得到的新的生成树所组成的集合,称为树T的邻集,记为N(T);
- *设T是图G的最小生成树,如果T1满足ω(T1)=min{ω(T’)|T’∈N(T)},则T1是G的次小生成树;
- *首先先求该图的最小生成树T,时间复杂度O(Vlog2V+E);
- *然后,求T的邻集中权值和最小的生成树,即图G 的次小生成树;
- *如果只是简单的枚举,复杂度很高;
- *首先枚举两条边的复杂度是O(VE),再判断该交换是否可行的复杂度是O(V),则总的时间复杂度是O(V2E);
- *分析可知,每加入一条不在树上的边,总能形成一个环,只有删去环上的一条边,才能保证交换后仍然是生成树;
- *而删去边的权值越大,新得到的生成树的权值和越小,可以以此将复杂度降为O(VE);
- *更好的方法:首先做一步预处理,求出树上每两个结点之间的路径上的权值最大的边;
- *然后枚举图中不在树上的边,有了预处理,就可以用O(1)的时间得到形成的环上的权值最大的边;
- *预处理:因为是一棵树,只要简单的BFS即可,预处理所要的时间复杂度为O(V2);
下面是用prim算法写的代码:
这里面的用了一个path数组储存两点中权值最大的那条路径;
low数组保存边的权值;
用pre数组保存前驱的位置,用一个uesd数组标记是否在生成树中;
#include <cstdio> #include <cstring> #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) const int maxn=100+5; const int INF=0x3fffffff; int map[maxn][maxn],low[maxn]; int path[maxn][maxn],pre[maxn];//从i到j的路径上最大边的权值 int visit[maxn],used[maxn][maxn];//标记数组 int n,m; int prim() { int i,j,pos,mst=0; memset(visit,0,sizeof(visit));//初始化 memset(path,0,sizeof(path)); memset(used,0,sizeof(used)); visit[1]=1; for(i=1;i<=n;i++) { low[i]=map[1][i]; pre[i]=1;//记录前驱 } for(i=1;i<n;i++) { pos=-1; for(j=1;j<=n;j++) { if(!visit[j]&&(pos==-1||low[j]<low[pos])) pos=j; } used[pos][pre[pos]]=used[pre[pos]][pos]=1;//标记已经加入生成树 mst+=map[pos][pre[pos]];//权值相加 visit[pos]=1; for(j=1;j<=n;j++) { if(visit[j]&&j!=pos) path[pos][j]=path[j][pos]=max(path[j][pre[pos]],low[pos]);//求从pos到j权值最大的路径 if(!visit[j]&&low[j]>map[pos][j]) { low[j]=map[pos][j];//更新low数组 pre[j]=pos; } } } return mst; } int main() { int t,x,y,w,i,j; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(i=1;i<=n;i++) for(j=1;j<=n;j++) map[i][j]=INF;//把map初始化为INF for(i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&w); map[x][y]=map[y][x]=w; } int mst; int res=INF; mst=prim(); for(i=1;i<=n;i++) for(j=1;j<=n;j++) { if(i!=j&& !used[i][j]) res=min(res,mst+map[i][j]-path[i][j]);//生成次小生成树 } if(res==mst) puts("Not Unique!"); else printf("%d\n",mst); } return 0; }
如果生成的次小生成树和最小生成树相等说明不唯一,不相等说明唯一;
最小生成树中加入一条边,再删除最小生成树中的一条边,就得到次小生成树,这里主要进行了预处理,对权值较大的路径进行预处理。
这个博客的次小生成树讲的不错 http://www.cnblogs.com/hxsyl/p/3290832.html
kruskal待更新;
poj 1679 The Unique MST (次小生成树),布布扣,bubuko.com