Luogu P1550[USACO08OCT]打井Watering Hole

Description:

农民John 决定将水引入到他的n(1<=n<=300)个牧场。他准备通过挖若干井,并在各块田中修筑水道来连通各块田地以供水。在第i 号田中挖一口井需要花费W_i(1<=W_i<=100,000)元。连接i 号田与j 号田需要P_ij (1 <= P_ij <= 100,000 , P_ji=P_ij)元。请求出农民John 需要为使所有农场都与有水的农场相连或拥有水井所需要的钱数。

Analysis:

我们可以转换一下思路,把每个点的点权当成一个指向自己的边权,然后构造一棵最小生成树就好了!

Code

注意数组大小!

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define N 500
using namespace std;
struct edge{
	int from,to,w;
	bool operator < (const edge &a) const {
		return w < a.w;
	}
}e[100001];
int set[N],w[N],num_edge,n;
inline void add(int from,int to,int w)
{
	e[++num_edge].from = from;
	e[num_edge].to = to;
	e[num_edge].w = w;
}
int findset(int x)
{
	if(set[x] == x) return x;
	return set[x] = findset(set[x]);
}
void unionset(int a,int b)
{
	a = findset(a);
	b = findset(b);
	set[a] = b;
}
void solve()
{
	sort(e + 1,e + 1 + num_edge);
	int k = 0,ans = 0;
	for(int i = 1;i <= num_edge;++i)
	{
		int a = e[i].from,b = e[i].to;
		if(findset(a) != findset(b))
		{
			unionset(a,b);
			++k;
			ans += e[i].w;
		}
		if(k == n) break;
	}
	printf("%d
",ans);
}
int main()
{
	scanf("%d",&n);
	for(int i = 1;i <= n;++i)
	{
		scanf("%d",&w[i]);
		set[i] = i;
		add(0,i,w[i]);
	}
	for(int i = 1;i <= n;++i)
	{
		for(int j = 1;j <= n;++j)
		{
			int v;
			scanf("%d",&v);
			if(i > j)
			{
				add(i,j,v);
			}
		}
	}	
	solve();
	return 0;
}
岂能尽如人意,但求无愧我心
原文地址:https://www.cnblogs.com/Zforw/p/10809978.html