Tưới nước đồng cỏ
2017-08-04Bài gốc
Đề bài
Nông dân John quyết định mang nước tới cho N đồng cỏ của mình, để thuận tiện ta đánh số các đồng cỏ từ 1 đến N. Để tưới nước cho 1 đồng cỏ John có thể chọn 2 cách, 1 là đào ở đồng cỏ đó 1 cái giếng hoặc lắp ống nối dẫn nước từ những đồng cỏ trước đó đã có nước tới.
Để đào một cái giếng ở đồng cỏ i cần 1 số tiền là W_i. Lắp ống dẫn nước nối 2 đồng cỏ i và j cần 1 số tiền là P_ij.
Tính xem nông dân John phải chi ít nhất bao nhiêu tiền để tất cả các đồng cỏ đều có nước.
Giới hạn:
- 1 <= N <= 300
- 1 <= W_i <= 100,000
- 1 <= P_ij <= 100,000; P_ij = P_ji; P_ii=0
Định dạng test
Input:
- Dòng 1: Một số nguyên duy nhất: N
- Các dòng 2..N + 1: Dòng i+1 chứa 1 số nguyên duy nhất: W_i
- Các dòng N+2..2N+1: Dòng N+1+i chứa N số nguyên cách nhau bởi dấu cách; số thứ j là P_ij
Output:
- Dòng 1: Một số nguyên duy nhất là chi phí tối thiểu để cung cấp nước cho tất cả các đồng cỏ.
Ví dụ:
Có 4 đồng cỏ. Mất 5 tiền để đào 1 cái giếng ở đồng cỏ 1, 4 tiền để đào ở đồng cỏ 2, 3 và 3 tiền để đào ở đồng cỏ 4. Các ống dẫn nước tốn 2, 3, và 4 tiền tùy thuộc vào nó nối đồng cỏ nào với nhau.
Nông dân John có thể đào 1 cái giếng ở đồng cỏ thứ 4 và lắp ống dẫn nối đồng cỏ 1 với tất cả 3 đồng cỏ còn lại, chi phí tổng cộng là 3 + 2 + 2 + 2 = 9.
Thuật toán
Thêm một đỉnh (n+1), chi phí của tất cả các đỉnh từ 1 đến n đến đỉnh mới này chính bằng chi phí đào giếng tại đỉnh đó, tức là: p[n+1][i] = p[i][n+1] = w[i].
Đáp án là tổng trọng số của cây khung nhỏ nhất của đồ thị.
Source code
Ngôn ngữ: C++.
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
typedef pair<ll, ii> lii;
const int N = 305;
int n, lab[N];
ll w[N], p[N][N];
vector<lii> edges;
int root(int u) {
return (lab[u] < 0) ? u : lab[u] = root(lab[u]);
}
void merge(int u, int v) {
u = root(u);
v = root(v);
if (u == v) return;
if (lab[u] > lab[v]) swap(u, v);
lab[u] += lab[v];
lab[v] = u;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
// input
cin >> n;
FOR(i,1,n) {
cin >> w[i];
p[i][n+1] = w[i];
p[n+1][i] = w[i];
}
FOR(i,1,n) FOR(j,1,n) cin >> p[i][j];
// edge
n++;
FOR(i,1,n-1)
FOR(j,i+1,n)
edges.pb(mp(p[i][j], mp(i, j)));
// kruskal init
RESET(lab, -1);
sort(edges.begin(), edges.end());
// kruskal + solve
ll cost = 0;
int cnte = 0;
for(auto e : edges) {
ll p = e.fi;
int u = e.se.fi;
int v = e.se.se;
if (root(u) != root(v)) {
merge(u, v);
cost += p;
cnte++;
if (cnte == n-1) break;
}
}
cout << cost;
return 0;
}
Độ phức tạp
Xấp xỉ O(MlogM + MlogN), với M = N*N/2.