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


 

 

發佈了331 篇原創文章 · 獲贊 17 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章