1. 程式人生 > 實用技巧 >紀中暑假集訓 2020.08.08【NOIP提高組】模擬 T2:【Usaco2008 Oct 資格賽】灌水

紀中暑假集訓 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;
}