1. 程式人生 > >BZOJ1601 [Usaco2008 Oct]灌水

BZOJ1601 [Usaco2008 Oct]灌水

題目描述:

Farmer John已經決定把水灌到他的n(1<=n<=300)塊農田,農田被數字1到n標記。

把一塊土地進行灌水有兩種方法,從其他農田飲水,或者這塊土地建造水庫。 建造一個水庫需要花費wi(1<=wi<=100000),連線兩塊土地需要花費Pij(1<=pij<=100000,pij=pji,pii=0).

計算Farmer John所需的最少代價。

 

題解:

最小生成樹經典題。

我們建立一個超級源點,將每個點與超級源點建邊,邊權為每個點建立水庫的費用。

接下來我們只需要在新圖上做最小生成樹即可。

附上程式碼:

#include<cstdio>
#include
<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> using namespace std; int n,idx,ans,k,fa[1001]; bool vis[301][301]; struct Edge { int x,y,v; }f[100001]; bool cmp(const Edge &a,const Edge &b) { return a.v<b.v; } int
find(int p) { if(fa[p]!=p) fa[p]=find(fa[p]); return fa[p]; } void merge(int x,int y) { int fx=find(x),fy=find(y); if(fx!=fy) fa[fx]=fy; } int main() { scanf("%d",&n); for(int i=1;i<=n+1;i++) fa[i]=i; for(int i=1;i<=n;i++) {
int a; scanf("%d",&a); f[++idx].x=i; f[idx].y=n+1; f[idx].v=a; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { int a; scanf("%d",&a); if(a==0||vis[i][j]==1) continue; f[++idx].x=i; f[idx].y=j; f[idx].v=a; vis[i][j]=vis[j][i]=1; } sort(f+1,f+idx+1,cmp); for(int i=1;i<=idx;i++) { if(find(f[i].x)!=find(f[i].y)) { merge(f[i].x,f[i].y); ans+=f[i].v; k++; } if(k==n) break; } printf("%d",ans); }