Poj 1679 The Unique MST (最小生成树唯一性判定)

2014-5-11更新

本题比较靠谱的解法是用次小生成树解。

具体思想可参考:http://blog.csdn.net/jarily/article/details/8912538

以下分析也转自上面的博客。代码按照自己的风格重写了。

基于 Kruscal 的写法可以参考 http://yzmduncan.iteye.com/blog/1018358

题目大意:
 *给出一个连通无向图,判断它的最小生成树是否唯一;
 *如果唯一,输出生成树的大小,否则输出"Not Unique!";
 *
 *算法思想:
 *本题可以尝试求与最小生成树权值相等的树是否存在;
 *但是更好的思路是直接求次小生成树,如果次小生成树等于最小生成树;
 *则说明最小生成树不唯一,否则最小生成树一定是唯一的;

#include <cstdio>
#include <cstring>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))

const int INF=0x3fffffff;
const int N=105;

int graph[N][N],dis[N];
int path[N][N];//从i到j的路径上最大边的权值
int pre[N];  //辅助数组,记录前驱
bool visit[N],used[N][N];//边是否在该MST中
int n,m;

int Prim ()
{
	int Mst=0,i,j;
	visit[1]=true;
	for (i=1; i<=n; ++i)
	{
		dis[i]=graph[1][i];
		pre[i]=1;
	}
	for (i=1;i<n;i++)
	{
		int k=-1;
		for (j=1; j<=n; ++j)
			if (visit[j]==false)
			{
				if (k==-1 || dis[j]<dis[k])
					k=j;
			}
		used[k][pre[k]]=used[pre[k]][k]=true;//加入MST
		Mst+=graph[pre[k]][k];
		visit[k]=true;
		for (j=1;j<=n;j++)
		{
			if (visit[j] && j!=k) //求从k到j的路径上最大边的权值
				path[k][j]=path[j][k]=max(path[j][pre[k]],dis[k]);
			if (visit[j]==false)
				if (dis[j]>graph[k][j]) //更新相邻顶点的dis
				{
					dis[j]=graph[k][j];
					pre[j]=k;
				}
		}
	}
	return Mst;
}

void Init ()
{
	for (int i=0;i<=n;i++)
		for (int j=i+1;j<=n;j++)
			graph[i][j]=graph[j][i]=INF;
	memset(visit,false,sizeof(visit));
	memset(used,false,sizeof(used));
	memset(path,0,sizeof(path));
}

int main ()
{
    int T;
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d%d",&n,&m);
		Init();
		int u,v,w,i;
		for (i=1;i<=m;i++)
		{
			scanf("%d%d%d",&u,&v,&w);
			graph[u][v]=graph[v][u]=w;
		}
		int Mst=Prim();
		int ans=INF;
		for (i=1;i<=n;i++)
			for (int j=1;j<=n;j++)
                if (i!=j && used[i][j]==false)
					ans=min(ans,Mst+graph[i][j]-path[i][j]);
		if (ans==Mst)
			printf("Not Unique!\n");
		else
			printf("%d\n",Mst);
	}
	return 0;
}

2014-5-4 更新 这题测试数据貌似非常弱。。。当时用下面提到的方法居然水过去了,下面给一组反例:

1

4 5

1 2 2

1 3 2

1 4 2

2 4 101

3 4 101


正好最近准备学次小生成树,学会后换方法……


题目链接:http://poj.org/problem?id=1679

题意:给定的图是否具有唯一的最小生成树。

思路:看到网上的代码都是用次小生成树解的,但其实不用那么麻烦。

利用Prim算法求最小生成树,选择最小边时进行判断:是否有两个或以上的未选择顶点到已选顶点集合的权值相等的,若有则最小生成树不唯一。同时在松弛计算的时候也要
对刚加进的顶点进行权值是否相等的判断。

#include <cstdio>
#include <cstring>

const int INF=0x7fffffff;

int map[105][105],dis[105];
int m,n;
bool visit[105];

int Prim ()  
{  
    int i,j,k;
    int temp,ans=0;
    for (i=1;i<=n;i++)
        dis[i]=map[1][i];
    visit[1]=true;
    for (i=1;i<n;i++)
    {  
        for (temp=INF,j=1;j<=n;j++)
            if (dis[j]<temp && visit[j]==false)
                temp=dis[k=j];
        ans+=temp;
        visit[k]=true;
		for (j=1;j<=n;j++)
			if (visit[j]==false && dis[j]==temp)
				return -1;
        for (j=1;j<=n;j++)
             if (visit[j]==false)
				 if (dis[j]>map[k][j])
					 dis[j]=map[k][j];
				 else if (dis[j]==map[k][j] && dis[j]!=INF)
					 return -1;
    }  
    return ans;  
}

void init ()
{
	int i;
	for (i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			map[i][j]=INF;
	for (i=1;i<=n;i++)
		map[i][i]=0;
	memset(visit,false,sizeof(visit));
}

int main ()
{
	int T;
	scanf("%d",&T);
	while (T--)
	{
		int x,y,w;
		scanf("%d%d",&n,&m);
		init ();
		for (int i=0;i<m;i++)
		{
			scanf("%d%d%d",&x,&y,&w);
			map[x][y]=w;
			map[y][x]=w;
		}
		int ans=Prim();
		if (ans == -1)
			printf("Not Unique!\n");
		else
			printf("%d\n",ans);
	}
	return 0;
}


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值