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 ;
}