nyoj118 修路方案(求次小生成树)



修路方案

时间限制: 3000 ms  |  内存限制: 65535 KB
难度: 5
描述

南将军率领着许多部队,它们分别驻扎在N个不同的城市里,这些城市分别编号1~N,由于交通不太便利,南将军准备修路。

现在已经知道哪些城市之间可以修路,如果修路,花费是多少。

现在,军师小工已经找到了一种修路的方案,能够使各个城市都联通起来,而且花费最少。

但是,南将军说,这个修路方案所拼成的图案很不吉利,想让小工计算一下是否存在另外一种方案花费和刚才的方案一样,现在你来帮小工写一个程序算一下吧。

输入
第一行输入一个整数T(1<T<20),表示测试数据的组数
每组测试数据的第一行是两个整数V,E,(3<V<500,10<E<200000)分别表示城市的个数和城市之间路的条数。数据保证所有的城市都有路相连。
随后的E行,每行有三个数字A B L,表示A号城市与B号城市之间修路花费为L。
输出
对于每组测试数据输出Yes或No(如果存在两种以上的最小花费方案则输出Yes,如果最小花费的方案只有一种,则输出No)
样例输入
2
3 3
1 2 1
2 3 2
3 1 3
4 4
1 2 2
2 3 2
3 4 2
4 1 2
样例输出
No
Yes
来源
POJ题目改编
上传者
张云聪

对于最小生成树(可以用kruskal和prime算法求得,在这里我是用kruskal求得,如果不会请自己百度。),边的权值的和最小称为最小生成树。

而次小生成树就是除了最小生成树外的最小生成树。而且所有的次小生成树都是通过最小生成树的换边得到的。

所以难点就是如何换边。

对于如何换边:

1.先求出最小生成树,值为x。

2.一一枚举添加不在生成树上的边(这时候一定形成了一个环)

3.寻找环上的(最小生成树上的边)权值最大值与你所添加不在生成树上的边的权值比较,所得到的差值为min。

由于是一一枚举添加边,min有多个,求出最小的哪一个,所以次小生成树就为x+min。

所以对于这道题也就很简单了,只要min=0就代表有多种方案了。具体请看代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
struct node
{
	int a,b,c,vis;
}c[200005];
int fa[505],v,e,stamp[505][505],visit[505];
bool cmp(node a,node b)
{
	return a.c<b.c;
}
int find(int x)
{
	if(fa[x]!=x) fa[x]=find(fa[x]);
	return fa[x];
}
void init()
{
	for(int i=1;i<=v;i++)
	fa[i]=i;
}
void comb(int x1,int x2,int cost)//存贮最小生成树的边
{
	stamp[x1][x2]=stamp[x2][x1]=cost;
}
int sec_find(int x,int y,int min)//换边
{
	if(x==y)
	return min;
	for(int i=1;i<=v;i++)
	{
		if(stamp[x][i]&&!visit[i])
		{
			visit[x]=1;
			if(stamp[x][i]>min)
			min=stamp[x][i];
			sec_find(i,y,min);
		}
	}
	return min;
}
int main()
{
	int ncase,mintree,sec;
	scanf("%d",&ncase);
	while(ncase--)
	{
		memset(stamp,0,sizeof(stamp));
		memset(visit,0,sizeof(visit));
		scanf("%d %d",&v,&e);
		init();
		for(int i=0;i<e;i++)
		scanf("%d %d %d",&c[i].a,&c[i].b,&c[i].c);
		sort(c,c+e,cmp);
		int mintree=0;
		for(int i=0;i<e;i++)//求最小生成树
		{
			int x1=find(c[i].a);
			int x2=find(c[i].b);
			if(x1!=x2)
			fa[x1]=x2,mintree+=c[i].c,c[i].vis=1,comb(x1,x2,c[i].c);
			else
			c[i].vis=0;//对于不在最小生成树里面的边标记一下
		}
		int flag=0;
		for(int i=0;i<e;i++)
		if(!c[i].vis)//最小生成树之外的边
		{
			sec=sec_find(c[i].a,c[i].b,-1000000);
			if(sec==c[i].c)
			{
				flag=1;
				break;
			}
		}
		if(flag)
		printf("Yes\n");
		else
		printf("No\n");
	}
	return 0;
}


你可能感兴趣的:(最小生成树,dijkstra算法,nyoj,118,次小生成树)