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:感觉此题的思路太过巧妙,好难进行最恰当的思路概括。尽力意会。留坑,待思路再整理。