bzoj1601(MST+优秀建图==好题)

Farmer John已经决定把水灌到他的n(1<=n<=300)块农田,农田被数字1到n标记。把一块土地进行灌水有两种方法,从其他农田饮水,或者这块土地建造水库。 建造一个水库需要花费wi(1<=wi<=100000),连接两块土地需要花费Pij(1<=pij<=100000,pij=pji,pii=0). 计算Farmer John所需的最少代价。

 

刚开始并没有想到是最小生成树,尽管曾经考虑过,但是感觉无法描述整个题目的所需决策。并没有多想。

实际上,就是每个水库要么选择自己这里建造水库,要么选择连一条边。

我们想如果所有的水库最终都选择好了一个决策的话,那么整个图就是,分成m块,每一块有一个点是自己建造水库的。其他都是顺着边连到这个点的。也就是在这个子图当中做最小生成树。这之后就真的是比较难想了。

经典的解法是加一个超级源,每个点向源连花费w【i】边。

然后在整个图中做最小生成树。超级源的连通保证了至少有一个点建造了水库

漂亮!太妙了!

 

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cctype>
using namespace std;
const int N=305;
int n,tot;
struct aa
{
	int x,y,dis;
	bool operator <(const aa &b)const 
	{
		return dis<b.dis;
	}
}bian[N*N];

void addedge(int x,int y,int z)
{
	bian[++tot].dis=z;bian[tot].x=x;bian[tot].y=y;
}
int fa[N];
int getfa(int x)
{
	return x==fa[x] ? x: fa[x]=getfa(fa[x]);
}

int main()
{
	scanf("%d",&n);
	int x;
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&x);
		addedge(i,n+1,x);
	}
	
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
		{
			scanf("%d",&x);
			if (j<i) addedge(i,j,x);
		}
	sort(bian+1,bian+tot+1);
	for (int i=1;i<=n+1;i++) fa[i]=i;
	int ans=0;
	for (int i=1;i<=tot;i++)
	{
		int fx=getfa(bian[i].x),fy=getfa(bian[i].y);
		if (fx!=fy)
		{
			fa[fx]=fy;
			ans+=bian[i].dis;
		}
	}
	
	printf("%d",ans);
	return 0;
}

总结

1:本题是最小生成树问题的经典模型,实际上加超级源的方法网络流差分约束,还有这个MST当中,有时就会产生非常关键的作用。(别的图论问题也许也有,暂时没有发现)

2:感觉此题的思路太过巧妙,好难进行最恰当的思路概括。尽力意会。留坑,待思路再整理。


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值