Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 21119 | Accepted: 7451 |
Description
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!
题意:求最小生成树是否唯一。
什么是生成树:就是包含1~n,n个节点的一个数
什么是次小生成树,就是在图中生成树(权值)中仅仅大于最小生成树(权值)的一棵树。
分析:设T为图的一颗最小生成树,那么我们思考发现,只要我们能够找到不在T中一条边(U,V)来替换T中的(U,v)那么这个替换过的树,必定是一个生成树,又因为最小生成树中的任意两个节点的边都是相对最短的,那么我们只需要不断的换边的话(每次只换一条边),这就形成了一个由最小生成树改变得来的生成树集,叫T的邻集,那么我们不难发现,次小生成树一定是在T的邻集中,所以我们只需要枚举T的邻集找出来次小生成树,然后判断是不是与最小生成树相等即可。
那么问题来了:挖掘机技术哪家强。。咳咳 是怎么求呢?
求法:每次枚举一条在T中的边,找出在T中连接u和v的唯一路径(肯定是唯一的,,想想为啥),在路径中找出最长的那条边,用max【u】【v】储存, 然后枚举不在T中的边u, v,替换掉最长边max【u】【v】,不断枚举,
有点抽象,我说一下我的理解
图有点丑,不过能表达我的意思。MST就是最小生成树,SST就是次小生成树 u= 2, v = 3.
转换过后就是这样。
其实这道题有二种解法的,一种是求出来次小生成树,一种是标记最小生成树的边,一次删去标记的边,求最小生成树,
注:这只是我个人(小菜鸟一枚)的理解,欢迎高手来指教!!
参考:http://www.cnblogs.com/dongsheng/articles/2617186.html
代码1(次小生成树):
#include <cstdio> #include <cstring> //#include <algorithm> const int M = 105; const int INF = 0x3f3f3f3f; using namespace std; int map[M][M], n, m, low[M]; int pre[M], max[M][M]; //pre是上一个点,max就是储存最长的路径 bool con[M][M], vis[M]; con数组是标记最小生成树中的边 int Max(int a, int b){ return a>b?a:b; } int prim(){ memset(vis, 0, sizeof(vis)); memset(max, 0, sizeof(max)); memset(con, false, sizeof(con)); int i, j, pos, min, sum = 0; pos = 1; for(i = 1; i <= n; i ++){ low[i] = map[pos][i]; pre[i] = pos; } vis[pos] = 1; for(i = 1; i < n; i ++){ min = INF; for(j = 1; j <= n; j ++){ if(!vis[j]&&min > low[j]){ min = low[j]; pos = j; } } if(min == INF) return -1; sum += min; vis[pos] = 1; con[pre[pos]][pos] = con[pos][pre[pos]] = 1; max[pre[pos]][pos] = max[pos][pre[pos]] = min; //先储存min for(j = 1; j <= n; j ++){ //这里的循环就是找出来最长边,仔细想一想哈~~用的是的dp max[j][pos] = Max(max[pre[pos]][pos], max[j][pos]); } for(j = 1; j <= n; j ++){ if(!vis[j]&&map[pos][j] < low[j]){ low[j] = map[pos][j]; pre[j] = pos; } } } return sum; } int main(){ int t; scanf("%d", &t); while(t --){ scanf("%d%d", &n, &m); int i, j; for(i = 0; i < M; i ++) for(j = 0; j < M; j ++) map[i][j] = INF; int a, b, c; for(i = 0; i < m; i ++){ scanf("%d%d%d", &a, &b, &c); map[a][b] = map[b][a] = c; } int res = prim(); int flag = 0; for(i = 1; i <= n; i ++){ for(j = 1; j <= n; j ++){ if(con[i][j]||map[i][j] == INF) continue; //这里就是枚举最小生成树的边, int ans = map[i][j] - max[i][j]; if(ans == 0){ flag = 1; break; } } if(flag) break; } if(flag) printf("Not Unique!\n"); else printf("%d\n", res); } return 0; }
#include <cstdio> #include <cstring> #include <algorithm> const int M = 105; using namespace std; struct node{ int from, to, w; bool flag; }s[M*M]; int n, m, fat[M]; int f(int x){ if(x != fat[x]) fat[x] = f(fat[x]); return fat[x]; } int cmp(node a, node b){ return a.w < b.w; } int kruskal(){ int i, min = 0, cou = 0; for(i = 0; i < M; i ++) fat[i] = i; for(i = 0; i < m; i ++){ int x = f(s[i].from); int y = f(s[i].to); if(x != y){ min += s[i].w; fat[x] = y; s[i].flag = true; cou ++; } } if(cou != n-1) return -1; return min; } int kruskalsst(int v){ int i, min = 0, cou = 0; for(i = 0; i < M; i ++) fat[i] = i; for(i = 0; i < m; i ++){ if(v == i) continue; int x = f(s[i].from); int y = f(s[i].to); if(x != y){ min += s[i].w; fat[x] = y; cou ++; } } if(cou != n-1) return -1; return min; } int main(){ int t; scanf("%d", &t); while(t --){ scanf("%d%d", &n, &m); memset(s, 0, sizeof(s)); int i; for(i = 0; i < m; i ++){ scanf("%d%d%d", &s[i].from, &s[i].to, &s[i].w); } sort(s, s+n, cmp); int res = kruskal(); if(res == -1 ){ printf("Not Unique!\n"); continue; } int flag = 0; for(i = 0; i < m; i ++){ if(s[i].flag){ int ans = kruskalsst(i); if(res == ans) { flag = 1; break; } } } if(flag) printf("Not Unique!\n"); else printf("%d\n", res); } return 0; }
poj 1679 The Unique MST 【次小生成树】
原文地址:http://blog.csdn.net/shengweisong/article/details/41593549