http://poj.org/problem?id=1679
题意:给定一个图, 求最小生成树,若最小生成树唯一则输出最小生成树的代价,否则输出“Not Unique!” 。
思路:先求出最小生成树,然后求次小生成树,判断次小生成树和最小生成树的代价是否相等。
以下说下用Prim求次小生成树的想法:
算法1、step 1. 先用prim求出最小生成树T.
在prim的同时,用一个矩阵max[u][v] 记录 在T中连结任意两点u,v的唯一的
路中权值最大的那条边的权值. (注意这里).
这是很容易做到的,因为prim是每次增加一个结点s, 而设已经标号了的结点
集合为W, 则W中所有的结点到s的路中的最大权值的边就是当前加入的这条边.
step 1 用时 O(V^2).
step 2. 枚举所有不在T中的边uv, 加入边uv则必然替换权为max[u][v]的边。
算法2、 先用prim求出最小生成树T。
枚举T中的每一条边,把它删除,求剩下的图的最小生成树。选所有枚举得到的生成树中的最小的那一个。
代码:
#include<stdio.h> #include<string.h> const int MAXN = 110 ; const int INF = 100000000 ; int N ,M , T; int map[MAXN][MAXN] ,dis[MAXN]; bool vis[MAXN] ,choose[MAXN][MAXN]; int ans ,pre[MAXN] ,max[MAXN][MAXN] ; void Prim(){ ans = 0 ; for(int i=1;i<=N;i++){ vis[i] = 0 ; dis[i] = map[1][i] ; pre[i] = 1 ; } memset(choose , 0 ,sizeof(choose)); vis[1] = 1 ; for(int i=1;i<N;i++){ int _min = INF , min_n ; for(int j=1;j<=N;j++){ if(vis[j]==1) continue ; if(_min > dis[j]){ _min = dis[j] ; min_n = j ; } } for(int j=1;j<=N;j++){ if(vis[j] == 0) continue ; max[j][min_n] = max[min_n][j] = dis[min_n] ; } vis[min_n] = 1 ; choose[min_n][pre[min_n]] = 1 ; choose[pre[min_n]][min_n] = 1; ans += dis[min_n] ; for(int i=1;i<=N;i++){ if(vis[i] == 1) continue ; if(dis[i] > map[min_n][i]){ dis[i] = map[min_n][i] ; pre[i] = min_n ; } } } } bool unique(){ for(int i=1;i<=N;i++){ for(int j=i+1;j<=N;j++){ int res = ans ; if(map[i][j]!=INF && choose[i][j]==0){ res = res + map[i][j] - max[i][j] ; if(res == ans) return false ; } } } return true ; } int main(){ int a ,b, c ; scanf("%d",&T); while(T--){ scanf("%d %d",&N,&M); for(int i=1;i<=N;i++){ for(int j=1;j<=N;j++){ if(i == j) map[i][j] = 0 ; else map[i][j] = INF ; } } for(int i=1;i<=M;i++){ scanf("%d %d %d",&a,&b,&c); if(map[a][b] > c){ map[a][b] = map[b][a] = c ; } } Prim() ; if(unique()){ printf("%d\n",ans); } else printf("Not Unique!\n"); } return 0 ; }