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:感覺此題的思路太過巧妙,好難進行最恰當的思路概括。盡力意會。留坑,待思路再整理。