POJ 1679 The Unique MST 次小生成树

来源:http://poj.org/problem?id=1679

题意:判断最小生成树是否唯一。

思路:求出最小生成树后再求次小生成树,如果次小生成树的长度和最小生成树的长度相等,则最小生成树不唯一,否则最小生成树唯一。

介绍一下我求次小生成树的方法。

次小生成树可以用kruskal和prime两种方法求。kruskal算法求的过程就是多次求最小生成树。我们第一次求最小生成树的时候把边的序号都标记下来,然后以后多次求最小生成树的时候,每次都不用第一次求得最小生成树的时候的边。比如说,第一次求最小生成树用到了边1 6 7,则我们再求3次最小生成树,第一次不用边1,第二次不用边6,第三次不用边7.。这样求三次,若中间有哪一次和第一次的的和相等,则说明不唯一。否则,唯一。这道题的话,倘若用kruskal算法的话,复杂度是2亿,会超。这道题可以用prime算法。

用prime算法求次小生成树的过程是先求一遍最小生成树,在求得过程中我们设置一个pre数组,表示的是某个点的前驱,比如说刚开始选到点1,然后选到的第一条边是从1到3的,则pre[3] = 1,并且在求最小生成树的过程中把选到的边记录下来。我们再设置另一个数组maxedge,表示在构造好的树中,如果x和y之间有一条边,且该边没有在最小生成树中,则maxedge[x][y] 表示加上这条边后,所能去掉的最大的边。比如说,我们求出一颗最小生成树后,此时x和y有一条边,且这条边没有被用到,此时maxedge[x][y]表示生成树中从x到y的路径中的最大的一条边,也就是说连接x和y的路径中能够去掉的最大的边。也就是说求出最小生成树后,在遍历图中所有的没有用到的边求次小生成树即可。如果某个值和最小生成树的值相同,则最小生成树不唯一。否则最小生成树唯一。

代码:

//1679
#include <iostream>
#include <cstdio>
#include <climits>
#include <string.h>
using namespace std;

#define CLR(arr,val) memset(arr,val,sizeof(arr))
const int N = 110;
int vis[N],map[N][N],numpoint,numroad,use[N][N],maxedge[N][N];
int len1,len2,pre[N];
int max(int a,int b){
	return a > b ? a:b;
}
int min(int a,int b){
	return a < b ? a:b;
}
void prime(){
	int dis[N],temp,pos = 0;
	CLR(vis,0);
	CLR(maxedge,0);
	for(int i = 1; i <= numpoint; ++i){
		dis[i] = map[1][i];
		pre[i] = 1;
	}
	dis[1]= 0;
	vis[1] = 1;
	len1 = 0;
	for(int i = 1; i <= numpoint; ++i){
	   temp = INT_MAX;
	   for(int j = 1; j <= numpoint; ++j){
		   if(temp > dis[j] && !vis[j]){
		     temp = dis[j];
			 pos = j;
		   }
	   }
	   if(temp == INT_MAX)
		   break;
	   vis[pos] = 1;
	   pre[pos] = i;
	   use[i][pos] = use[pos][i] = 0;
	   len1 += temp;
	   for(int j = 1; j <= numpoint; ++j){
	      if(vis[j])
			  maxedge[j][pos] = maxedge[pos][j] = max(maxedge[pre[pos]][j],map[pre[pos]][pos]);
		  if(!vis[j]){
		    if(dis[j] > map[pos][j]){
				dis[j] = map[pos][j];
				pre[j] = pos;
			}
		  }
	   }
	}
}
void second_prime(){
	len2 = INT_MAX;
	for(int i = 1; i <= numpoint; ++i){
		for(int j = 1; j <= numpoint; ++j){
			if(use[i][j]){
			  len2 = min(len2,len1 + map[i][j] - maxedge[i][j]);
			}
		}
	}
}
int main(){
	//freopen("1.txt","r",stdin);
	int numcase;
	scanf("%d",&numcase);
	while(numcase--){
		for(int i = 0; i < N; ++i)
			for(int j = 0; j < N; ++j)
				map[i][j] = INT_MAX;
		CLR(use,0);
		CLR(maxedge,0);
		CLR(pre,0);
	  scanf("%d%d",&numpoint,&numroad);
	  int x,y,z;
	  for(int i = 0; i < numroad; ++i){
	    scanf("%d%d%d",&x,&y,&z);
		map[x][y] = map[y][x] = z;
		use[x][y] = use[y][x] = 1;
	  }
	  prime();
	  second_prime();
	  if(len1 == len2)
		  printf("Not Unique!\n");
	  else{
		  printf("%d\n",len1);
	  }
	}
	return 0;
}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值