纪中暑假集训 2020.08.08【NOIP提高组】模拟 T2:【Usaco2008 Oct 资格赛】灌水

【Usaco2008 Oct 资格赛】灌水

Description

Farmer John已经决定把水灌到他的n(1<=n<=300)块农田,农田被数字1到n标记。把一块土地进行灌水有两种方法,从其他农田饮水,或者这块土地建造水库。

建造一个水库需要花费wi(1<=wi<=100000),连接两块土地需要花费Pij(1<=pij<=100000,pij=pji,pii=0).

计算Farmer John所需的最少代价。

Input

第一行:一个数n

第二行到第n+1行:第i+1行含有一个数wi

第n+2行到第2n+1行:第n+1+i行有n个被空格分开的数,第j个数代表pij。

Output

第一行:一个单独的数代表最小代价.

Sample Input

4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0

Sample Output

9

输出详解:

Farmer John在第四块土地上建立水库,然后把其他的都连向那一个,这样就要花费3+2+2+2=9

反思&题解

比赛&正解思路: (我是绝对不会告诉你我之前做过这道题……) 模型很显然是一个最小生成树,将每个点与一个0号节点连一个边,边权是建造水库的的价格,跑一边最小生成树就过了
反思: 做过的题不切就不对了吧

CODE

#include<bits/stdc++.h>
using namespace std;
struct arr
{
	int u,v,w;
}a[500005];
int n,tot,num,ans,fa[305];
bool cmp(arr x,arr y)
{
	return x.w<y.w;
}
int find(int k)
{
	if (fa[k]==k) return k;
	else
	{
		fa[k]=find(fa[k]);
		return fa[k];
	}
}
int main()
{
	scanf("%d",&n);
	int i;
	for (i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&x);
		a[++tot].u=0;
		a[tot].v=i;
		a[tot].w=x;
		a[++tot].u=i;
		a[tot].v=0;
		a[tot].w=x;
	}
	for (i=1;i<=n;i++)
	{
		int j;
		for (j=1;j<=n;j++)
		{
			int x;
			scanf("%d",&x);
			if (i!=j)
			{
				a[++tot].u=i;
				a[tot].v=j;
				a[tot].w=x;
			}
		}
	}
	for (i=0;i<=n;i++)
		fa[i]=i;
	sort(a+1,a+1+tot,cmp);
	for (i=1;i<=tot;i++)
	{
		int t1=find(a[i].u),t2=find(a[i].v);
		if (t1!=t2)
		{
			num++;
			ans+=a[i].w;
			fa[t1]=t2;
			if (num==n) break;
		}
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/CMC_YXY/article/details/107880818