[Usaco2008 Oct]灌水
题目描述
Farmer John已经决定把水灌到他的n(1<=n<=300)块农田,农田被数字1到n标记。把一块土地进行灌水有两种方法,从其他农田饮水,或者这块土地建造水库。 建造一个水库需要花费wi(1<=wi<=100000),连接两块土地需要花费Pij(1<=pij<=100000,pij=pji,pii=0). 计算Farmer John所需的最少代价。
输入格式
*第一行:一个数n
*第二行到第n+1行:第i+1行含有一个数wi
*第n+2行到第2n+1行:第n+1+i行有n个被空格分开的数,第j个数代表pij。
输出格式
*第一行:一个单独的数代表最小代价.
样例输入
4 5 4 4 3 0 2 2 2 2 0 3 3 2 3 0 4 2 3 4 0
样例输出
9 输出详解: Farmer John在第四块土地上建立水库,然后把其他的都连向那一个,这样就要花费3+2+2+2=9
这道题是要把所有的点连在一起,也就是要用最小生成树。直接用kruskal跑一遍就好了。
#include<algorithm> #include<cstdio> #include<cmath> #include<iostream> #include<cstring> using namespace std; const int maxn = 100005; int n; struct edge { int x, y, val; } e[maxn]; int ans, fa[305], t, cnt; int cmp(const edge &a, const edge &b) { return a.val < b.val; } int find(int x) { if(fa[x] != x) fa[x] = find(fa[x]); return fa[x]; } void get(int x, int y, int w) { cnt++; e[cnt].x = x; e[cnt].y = y; e[cnt].val = w; } int main() { scanf("%d", &n); for(int i = 1; i <= n; i++) { fa[i] = i; scanf("%d", &t); get(0,i,t); } for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { scanf("%d", &t); if(i != j) { get(i,j,t); } } sort(e + 1,e + cnt + 1,cmp); for(int i = 1; i <= cnt; i++) { if(find(e[i].x) != find(e[i].y)) { fa[find(e[i].x)] = find(e[i].y); ans += e[i].val; } } printf("%d\n", ans); return 0; }