Farmer John已经决定把水灌到他的n(1<=n<=300)块农田,农田被数字1到n标记。把一块土地进行灌水有两种方法,从其他农田饮水,或者这块土地建造水库。 建造一个水库需要花费wi(1<=wi<=100000),连接两块土地需要花费Pij(1<=pij<=100000,pij=pji,pii=0). 计算Farmer John所需的最少代价。
看起来挺难?
其实稍微有点变形的题目而已。
我们虚拟出来一个结点,这个结点跟每块农田连边,权值分别为每块农田建造水库的花费。
然后其他边都正常连。
然后求最小生成树即可。
一般来说知道超级源点,这道题就可以随便屠了。
这种加虚拟结点的方法在图论的很多问题中都会出现。
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <cstdlib> #include <ctime> #include <vector> #include <map> #include <set> #define MAXN 500005 #define INF 1000000007 using namespace std; struct EDGE { int u, v, w; EDGE(){} EDGE(int _u, int _v, int _w){u = _u; v = _v; w = _w;} bool operator <(const EDGE &cmp)const { return w < cmp.w; } }edge[MAXN]; int n, fa[MAXN]; int val[MAXN]; int find(int x) { if(fa[x] == x) return x; int t = find(fa[x]); fa[x] = t; return t; } int main() { int x; scanf("%d", &n); for(int i = 1; i <= n; i++) fa[i] = i; for(int i = 1; i <= n; i++) scanf("%d", &val[i]); int cnt = 0; for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) { scanf("%d", &x); edge[cnt++] = EDGE(i, j, x); } for(int i = 1; i <= n; i++) edge[cnt++] = EDGE(i, n + 1, val[i]); sort(edge, edge + cnt); int ans = 0; for(int i = 0; i < cnt; i++) { int fx = find(edge[i].u); int fy = find(edge[i].v); if(fx != fy) { fa[fx] = fy; ans += edge[i].w; } } printf("%d\n", ans); return 0; }