UVa 12452 - Plants vs. Zombies HD SP

contents

  1. 1. Problem
  2. 2. Sample Input
  3. 3. Sample Output
  4. 4. Solution

Problem

給一個 N 個點的樹圖,在點上放置植物保護所有的邊,每個點上最多放置一個植物,請問最少花費為何?

  • 綠豆槍手:花費 100 元,保護相鄰一條邊。
  • 分裂碗豆:花費 175 元,保護相鄰兩條邊。
  • 楊桃:花費 500 元,保護相鄰所有邊。

Sample Input

1
2
3
4
5
6
2
2
0 1
3
0 1
1 2

Sample Output

1
2
$100
$175

Solution

將樹轉換成有根樹,對於每一個 node 當成 tree 去做考慮。

狀態 dp[node][1:protect edge from parent, 0:none]

以 node 以下作為一個 subtree 的最少花費,並且是否已經保護通往父親的邊。

效率 O(n)

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
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
#define MAXN 32767
vector<int> g[MAXN];
long long dp[MAXN][2]; // [node][1:protect edge from parent, 0:none]
#define INF (1LL<<50)
void dfs(int u, int p) {
dp[u][0] = dp[u][1] = INF;
long long cost[3] = {};
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (v == p) continue;
dfs(v, u);
cost[0] += dp[v][0];
cost[1] += dp[v][1];
cost[2] += min(dp[v][0], dp[v][1]);
}
dp[u][0] = min(dp[u][0], cost[1]);
dp[u][1] = min(dp[u][1], cost[1] + 100);
dp[u][1] = min(dp[u][1], cost[2] + 500);
long long c[2] = {INF, INF};
for (int i = 0; i < g[u].size(); i++) {// two edge with subnode and parent.
int v = g[u][i];
if (v == p) continue;
dp[u][1] = min(dp[u][1], cost[1] - dp[v][1] + dp[v][0] + 175);
if (-dp[v][1] + dp[v][0] < c[1]) {
c[1] = -dp[v][1] + dp[v][0];
if (c[0] > c[1]) swap(c[0], c[1]);
}
}
// two two edge with two subnodes
dp[u][0] = min(dp[u][0], cost[1] + c[0] + c[1] + 175);
}
int main() {
int testcase, n;
int u, v;
scanf("%d", &testcase);
while (testcase--) {
scanf("%d", &n);
for (int i = 0; i < n; i++)
g[i].clear();
for (int i = 1; i < n; i++) {
scanf("%d %d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs(0, -1);
printf("$%lld\n", min(dp[0][0], dp[0][1]));
}
return 0;
}