洛谷 题解 P1550 【[USACO08OCT]打井Watering Hole】
本题看似很难,实际上思路非常简单——如果你想通了。
首先有一个问题:图中有几个点?大部分的人会回答\(n\)个点。错了,有\(n+1\)个。
多出来的那个点在哪?关键在于你要理解每一个决策的意义。实际上,多出来的那个点是地下的天然矿泉水。当我们打井时,我们实际上是在往地下连边。理解了这一点,代码就没有任何难度了。
构图时,我们只需多加一个点,对于每个点\(i\),我们连边\(i→n+1\),边权为\(w_i\)。然后直接跑最小生成树就没了。就没了。(转载from here)
#include<bits/stdc++.h>
using namespace std;
const int MAXN=300+10;
const int MAXM=1e5;
int n,m;
int fa[MAXN];
struct Node
{
int u,v,w;
bool operator < (const Node &x) const
{
return x.w>w;
}
}edge[MAXM];
inline int read()
{
int tot=0;
char c=getchar();
while(c<'0'||c>'9')
c=getchar();
while(c>='0'&&c<='9')
{
tot=tot*10+c-'0';
c=getchar();
}
return tot;
}
inline int find(int k)
{
if(fa[k]==k)return k;
else return fa[k]=find(fa[k]);
}
inline int kruskal()
{
int tot=0,cnt=0;
for(int i=1;i<=n;i++)
fa[i]=i;
for(int i=1;i<=m;i++)
{
int fx=find(edge[i].u),fy=find(edge[i].v);
if(fx!=fy)
{
fa[fx]=fy;
tot++;
cnt+=edge[i].w;
}
if(tot==n-1)return cnt;
}
}
int main()
{
n=read();
int x;
for(int i=1;i<=n;i++)
{
x=read();
edge[++m].u=i;
edge[m].v=n+1;
edge[m].w=x;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
x=read();
if(i<j)
{
edge[++m].u=i;
edge[m].v=j;
edge[m].w=x;
}
}
}
n++;
sort(edge+1,edge+1+m);
printf("%d\n",kruskal());
return 0;
}