| 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