BZOJ_1601_[Usaco2008_Oct]_灌水_(最小生成树_Kruskal)

时间:2024-01-03 12:06:26

描述


http://www.lydsy.com/JudgeOnline/problem.php?id=1601

有\(n\)个田地需要灌溉,每个田地可以自己引水,花费为\(w[i]\),或者连接其他被灌溉的田地,花费为\(p[i][j]\),求最小花费.

分析


我第一眼看以为是dp,发现不对...

如果田地不能自己引水只能和其他田地连接,就是裸的最小生成树.

这里加了一个可以自己引水,相当于加了一个和所有点都有连边,且权值分别为\(w[i]\)的结点,然后求\(n+1\)个点的最小生成树即可.

 #include <bits/stdc++.h>
using namespace std; const int maxn=+;
struct edge{
int u,v,w;
edge(int u=,int v=,int w=):u(u),v(v),w(w){}
bool operator < (const edge &a) const { return w<a.w; }
}g[maxn*maxn];
int n,ect,ans;
int f[maxn];
inline int read(int &x){ x=;int k=;char c;for(c=getchar();c<''||c>'';c=getchar())if(c=='-')k=-;for(;c>=''&&c<='';c=getchar())x=x*+c-'';return x*=k; }
inline int find(int x){ return x==f[x]?x:f[x]=find(f[x]); }
int main(){
read(n);
for(int i=,w;i<=n;i++) g[++ect]=edge(,i,read(w)), f[i]=i;
for(int i=;i<=n;i++)for(int j=,w;j<=n;j++){
read(w);
if(j<i) g[++ect]=edge(i,j,w);
}
sort(g+,g+ect+);
for(int i=,j=n;i<=ect&&j;i++){
int fu=find(g[i].u),fv=find(g[i].v);
if(fu!=fv) f[fu]=fv, j--, ans+=g[i].w;
}
printf("%d\n",ans);
return ;
}