Uva 10600 ACM Contest and Blackout(次小生成树)

题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=1541

题意:求一个图的最小生成树和次小生成树。

题解:

/*
 *算法引入:
 *设G=(V,E,w)是连通的无向图,T是图G的一棵最小生成树;
 *如果有另一棵树T1,满足不存在树T’,ω(T’)<ω(T1),则称T1是图G的次小生成树;
 *
 *算法思想:
 *邻集的概念:由T进行一次可行交换得到的新的生成树所组成的集合,称为树T的邻集,记为N(T);
 *设T是图G的最小生成树,如果T1满足ω(T1)=min{ω(T’)|T’∈N(T)},则T1是G的次小生成树;
 *首先先求该图的最小生成树T,时间复杂度O(Vlog2V+E);
 *然后,求T的邻集中权值和最小的生成树,即图G 的次小生成树;
 *如果只是简单的枚举,复杂度很高;
 *首先枚举两条边的复杂度是O(VE),再判断该交换是否可行的复杂度是O(V),则总的时间复杂度是O(V2E);
 *分析可知,每加入一条不在树上的边,总能形成一个环,只有删去环上的一条边,才能保证交换后仍然是生成树;
 *而删去边的权值越大,新得到的生成树的权值和越小,可以以此将复杂度降为O(VE);
 *更好的方法:首先做一步预处理,求出树上每两个结点之间的路径上的权值最大的边;
 *然后枚举图中不在树上的边,有了预处理,就可以用O(1)的时间得到形成的环上的权值最大的边;
 *预处理:因为是一棵树,只要简单的BFS即可,预处理所要的时间复杂度为O(V2);
 * /

引用自:http://blog.csdn.net/jarily/article/details/8883858

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int VMAX=100+5;
const int EMAX=VMAX*VMAX;
const int INF=0x3f3f3f3f;
int g[VMAX][VMAX],maxd[VMAX][VMAX];//g---存图,maxd存树上两点之间路径的最大权
int vis[VMAX];
int dist[VMAX];
int pre[VMAX];//前驱
int use[VMAX][VMAX];//0表示没用过,1表示用过,2表示不存在
int n,m;
int firmst,secmst;
int prim()
{
	memset(maxd,0,sizeof(maxd));
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)
	{
		dist[i]=g[1][i];
		pre[i]=1;
	}
	firmst=0;
	vis[1]=1;
	for(int i=1;i<n;i++)
	{
		int p=-1,mmin=INF;
		for(int j=1;j<=n;j++)
		{
			if(!vis[j]&&mmin>dist[j])
			{
				mmin=dist[j];
				p=j;
			}
		}
		if(p!=-1)
		{
			use[pre[p]][p]=use[p][pre[p]]=1;
			for(int j=1;j<=n;j++)
			{
				if(vis[j])
				{
					maxd[j][p]=max(maxd[j][pre[p]],g[pre[p]][p]);
				}
			}
			vis[p]=1;
			firmst+=mmin;
			for(int j=1;j<=n;j++)
			{
				if(!vis[j]&&dist[j]>g[p][j])
				{
					dist[j]=g[p][j];
					pre[j]=p;
				}
			}
		}
	}
	return firmst;
}
int secMst()
{
	int mmin=INF;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(use[i][j]==0)
				mmin=min(mmin,g[i][j]-maxd[i][j]);
		}
	}
	return secmst=firmst+mmin;
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				g[i][j]=INF;
				use[i][j]=2;
			}
		}
		while(m--)
		{
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			g[a][b]=c;
			g[b][a]=c;
			use[a][b]=0;
			use[b][a]=0;
		}
		prim();
		secMst();
		printf("%d %d\n",firmst,secmst);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值