洛谷 题解 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;
}
posted @ 2019-07-03 19:56  hulean  阅读(123)  评论(0编辑  收藏  举报