BZOJ1601《[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。

输出格式

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

输入输出样例

输入样例

1
2
3
4
5
6
7
8
9
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0

输出样例

1
9

输出详解:

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

解析

很显然这道题需要最小生成树

那么是不是我们生成树之后加上根节点的 w 值就可以了?

显然不!

很容易就能举出反例:
最小生成树的根节点 w_1=99999 ,次小生成树的根节点 w_2=1 ,两个生成树答案之差 ans_1 - ans_2 = 1

那么我们就可以考虑建一个虚拟的编号为 n + 1 的点,对于所有的点 i w_i 为边权进行连接
最后直接跑最小生成树即可。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/* -- Basic Headers -- */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>

/* -- STL Iterators -- */
#include <vector>
#include <string>
#include <stack>
#include <queue>

/* -- External Headers -- */
#include <map>
#include <cmath>

/* -- Defined Functions -- */
#define For(a,x,y) for (int a = x; a <= y; ++a)
#define Forw(a,x,y) for (int a = x; a < y; ++a)
#define Bak(a,y,x) for (int a = y; a >= x; --a)

namespace FastIO {

inline int getint() {
int s = 0, x = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') x = -1;
ch = getchar();
}
while (isdigit(ch)) {
s = s * 10 + ch - '0';
ch = getchar();
}
return s * x;
}
inline void __basic_putint(int x) {
if (x < 0) {
x = -x;
putchar('-');
}
if (x >= 10) __basic_putint(x / 10);
putchar(x % 10 + '0');
}

inline void putint(int x, char external) {
__basic_putint(x);
putchar(external);
}
}


namespace Solution {
struct UnionFind {
static const int MAX_SIZ = 100000 + 10;

int U[MAX_SIZ];

UnionFind() {
For (i, 1, MAX_SIZ) U[i] = i;
}

int Find(int x) {
if (U[x] == x) return U[x];
return U[x] = Find(U[x]);
}

void Union(int x, int y) {
int xx = Find(x);
int yy = Find(y);
if (xx == yy) return;
U[x] = y;
}
};

struct Graph {
static const int MAXN = 1000 + 10;
static const int MAXM = 100000 + 10;

struct Node {
int nweight, now;

Node() { nweight = now = 0; }

bool operator < (const Node &that) const {
return nweight > that.nweight;
}
};

struct Edge {
int now, weight, next;
int raw_now, raw_next;

bool operator < (const Edge &that) const {
return weight < that.weight;
}
} edge[MAXM * 2];

int head[MAXN], dis[MAXN], cnt;

inline void addEdge(int prev, int next, int weight, bool isR = true) {
if (isR) { addEdge(next, prev, weight, false); }
edge[++cnt].now = next;
edge[cnt].weight = weight;
edge[cnt].next = head[prev];
head[prev] = cnt;

edge[cnt].raw_next = next;
edge[cnt].raw_now = prev;
}

inline Node NewNode(int nowWeight, int now) {
Node tmp;
tmp.nweight = nowWeight;
tmp.now = now;
return tmp;
}

inline void SPFA() {
memset(dis, 0x7f, sizeof(dis));
std::priority_queue<Node> q;
q.push(NewNode(0, 1));
dis[1] = 0;
while (!q.empty()) {
Node NowNode = q.top();
q.pop();
int now = NowNode.now;
for (int e = head[now]; e; e = edge[e].next) {
int to = edge[e].now;
if (dis[to] > dis[now] + edge[e].weight) {
dis[to] = dis[now] + edge[e].weight;
q.push(NewNode(dis[to], to));
}
}
}
}

inline int Kruskal() {
int ans = 0, tot = 0;
UnionFind u;
std::sort(edge + 1, edge + 1 + cnt);
for (int i = 1; i <= cnt; ++i) {
int eu = u.Find(edge[i].raw_now);
int ev = u.Find(edge[i].raw_next);
if (eu == ev) continue;
u.Union(eu, ev);
ans += edge[i].weight;

++tot;
if (tot == cnt - 1) break;
}
return ans;
}
} g1;

int n, m;
}

signed main() {
#define HANDWER_FILE
#ifndef HANDWER_FILE
freopen("testdata.in", "r", stdin);
freopen("testdata.out", "w", stdout);
#endif
using namespace Solution;
using FastIO::getint;
n = getint();
For (i, 1, n) g1.addEdge(i, n + 1, getint());
For (i, 1, n) {
For (j, 1, n) {
int p = getint();
g1.addEdge(i, j, p, false);
}
}
FastIO::putint(g1.Kruskal(), '\n');
return 0;
}