POJ 1679 The Unique MST 次小生成树Prim

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


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值