次小生成树 (POJ1679)

题意:判断一个图的最小生成树是否唯一

首先明确一点:最小生成树中任意加一条边必形成环,在该环中任意删除一条边,必定还是生成树,因为生成树的定义是n-1条边且无环。

步骤:1、遍历最小生成树的任意两点,连接该两点,再删除该两点所在环中的所有线段中的最长的那段,求出新树的总权值。

          2、求出所有新树的权值最小的那个树就是次小生成树,判断次小生成树的权值和最小生成树的权值是否相等,

               如果不相等说明最小生成树唯一。


那么如何求出任意两点所在环中所有线段中最长的那段的长度呢? 比如两点为a和b,a连在点c上,b连在d上,要想求ab所在环的线段最大值,可以求max(bc所在环的线段最大值,ac线段长)代码:Max[j][k] = Max[k][j] = max(Max[j][pre[k]], dis[k]);

验证该方程的正确性可参考以下执行过程:



完整代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

const int INF = 0x3f3f3f3f;
const int maxn = 105;
int pre[maxn];
bool vis[maxn];
bool used[maxn][maxn];
int mat[maxn][maxn];
int MAX[maxn][maxn];
int dis[maxn];
int n,m;

int prim(int cur)
{
	int index = cur;
	memset(vis,false,sizeof(vis));
	memset(used,false,sizeof(used));
	memset(MAX,0,sizeof(MAX));
	int sum = 0;
	for (int i = 1;i <= n; i++)
	{
		dis[i] = mat[cur][i];
		pre[i] = cur;
	}
	vis[cur] = true;
	for (int i = 1; i < n; i++)
	{
		int mindis = INF;
		for (int j = 1; j <= n; j++)
		{
			if (!vis[j] && mindis > dis[j])
			{
				mindis = dis[j];
				index = j;
			}
		}
		if (mindis == INF) return -1;
		sum += mindis;
		vis[index] = true;
		used[index][pre[index]] = true;//标记已连接的线段 
		used[pre[index]][index] = true;
		for (int j = 1; j <= n; j++)
		{
			if (vis[j] && j != index)//寻找j-index所在环的最长线段 
				MAX[j][index] = MAX[index][j] = max(MAX[j][pre[index]],dis[index]);
			if (!vis[j] && dis[j] > mat[index][j])
			{
				dis[j] = mat[index][j];
				pre[j] = index;
			}
		}
	}
	return sum;
}

int  main()
{
	int t;
	scanf("%d",&t);
	while (t--)
	{
		scanf("%d%d",&n,&m);
		int u,v,w;
		for (int i = 1; i <= n; i++)
			for (int j = i; j <= n; j++)
				mat[i][j] = mat[j][i] = INF;
		for (int i = 1; i <= m; i++)
		{
			scanf("%d%d%d",&u,&v,&w);
			mat[u][v] = mat[v][u] = w;
		}
		int ans = prim(1);
		if (ans == -1) puts("Not Unique!");//没有最小生成树的情况 
		else
		{
			int sum = INF;
			for (int i = 1; i <= n; i++)
				for (int j = 1; j <= n; j++)
					if (!used[i][j] && mat[i][j] != INF)
						sum = min(sum,ans + mat[i][j] - MAX[i][j]);//遍历任意两点 
			if (sum == ans) puts("Not Unique!");
			else printf("%d\n",ans);
		} 
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/bpdwn2017/article/details/79689067
今日推荐