您的位置:首页 > 产品设计 > UI/UE

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

2013-03-08 22:08 621 查看
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

,dis
;
int path

;//从i到j的路径上最大边的权值
int pre
;  //辅助数组,记录前驱
bool visit
,used

;//边是否在该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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: