poj1679-次小生成树

说下求次小生出树的思路。 先求得最小生成树。

然后枚举不在生成树上的边E(i),将它加入到最小生成树上,则一定会形成一个环,我们把这个环上的最大边删掉,还是一个生成树。得到其权值W(i)  。min(W(i))即是次小生成树

具体实现时这样更简单:求得最小生成树权值min.

求在最小生成树上,任意两点之间的路径上的最大边,并用二维数组maxed[][]记录下来。 (调用多次spfa()实现)。

枚举所有不在生成树上的边,将其加入到最小生成树,并删除掉该环中在最小生成树上的最大边

则可以得到权值为

ans[i] = min+edge[i].w-maxed[u][v]

取ans的最小值即是次小生成树。

 

#include "iostream"
#include "algorithm"
#include "queue"
using namespace  std;
#define  MAXSIZE 110
#define  INF 999999
int t,m,n;
struct node
{
	int u;
	int v;
	int w;
	int flag;
};
node edge[MAXSIZE*MAXSIZE];
queue<int>que;
int f[MAXSIZE];
int e;
int kt;//最小生成树的权值
int vis[MAXSIZE];
int maxed[MAXSIZE][MAXSIZE];
int head[MAXSIZE];
int next[MAXSIZE];
int dis[MAXSIZE];
void addnode(int u,int v,int w,int f)
{
	edge[e].u=u;
	edge[e].v=v;
	edge[e].w=w;
	edge[e].flag=f;
	next[e]=head[u];
	head[u]=e++;

}
bool cmp(node &a,node &b)
{
	return a.w<b.w;
}
int findset(int x)
{
	if (x==f[x])
		return x;
	else
		return f[x]=findset(f[x]);

}
int kruskal()
{
	sort(edge,edge+e,cmp);
	int tot=0;
	int i;
	for (i=1;i<=n;i++)
	{
		f[i]=i;
	}
	for (i=0;i<e;i++)
	{
		if (findset(edge[i].u)!=findset(edge[i].v))
		{
			//union
			f[findset(edge[i].v)]=findset(edge[i].u);
			tot+=edge[i].w;
			edge[i].flag=1;
		}
	}
	return tot;
}
void spfa(int s)
{
	while(!que.empty())
	{
		que.pop();
	}
	memset(vis,0,sizeof(vis));
	que.push(s);
	vis[s]=1;
	int i;
	for (i=1;i<=n;i++)
	{
		dis[i]=INF;
	}
	dis[s]=0;
	while(!que.empty())
	{
		int t=que.front();
		que.pop();
		for ( i=head[t];i!=-1;i=next[i])
		{
			
			if (edge[i].flag&&dis[edge[i].v]>dis[t]+edge[i].w)
			{
				dis[edge[i].v]=dis[t]+edge[i].w;
				if (maxed[s][edge[i].v]<edge[i].w)
				{
					maxed[edge[i].v][s]=maxed[s][edge[i].v]=edge[i].w;
				}
				if (!vis[edge[i].v])
				{
					vis[edge[i].v]=1;
					que.push(edge[i].v);
				}
			}
			
		}
	}
}
int judge()
{
	int min=INF;
	//枚举所有不在生成树上的边
	for (int i=0;i<e;i++)
	{
		if (!edge[i].flag)
		{
			//ans = min+edge[i].w-maxed[u][v]
			int tans=kt+edge[i].w-maxed[edge[i].u][edge[i].v];
			if (min>tans)
			{
				min=tans;
			}
		}
	}
	//cout <<min<<endl;
	return min==kt;
}
int main()
{
	//freopen("in.txt","r",stdin);
	scanf("%d",&t);
	while(t--)
	{
		e=0;
		scanf("%d%d",&n,&m);
		memset(head,-1,sizeof(head));
		memset(next,-1,sizeof(next));
		memset(maxed,-1,sizeof(maxed));
		while(m--)
		{
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			addnode(a,b,c,0);
		}
		//求得最小生成树的权值
		 kt=kruskal();
	//	cout<<"kt: "<<kt<<endl;
		
		//bfs 求得点遍历生成树最大的边
		for (int i=1;i<=n;i++)
		{
			maxed[i][i]=0;
			spfa(i);
		}
		//判断是否具有次小生成树
		if (judge())
			printf("Not Unique!\n");
		else
			printf("%d\n",kt);

	}
	return 0;
}

 

 

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值