ζ

POJ1947-Rebuilding Roads

    题解

题目链接

这是一场比赛的一道题。看到第一眼,就直到这题是树形DP。但我已经很长一段时间没写过树形DP了,而且之前练习的时候,对于这种背包类的树形DP问题,我也没有练习得太多,这道题又不是纯模板的树上背包,因此想了老半天。让我不禁怀疑之前花费了大力气练习的树形DP是否毫无意义。

这棵树是无根树,需要对每个结点为根各进行一次动态规划,最后取以各个结点为根的最小值。在进行动态规划之前,需要求出以当前点为根的情况下,各个点的子树大小sum[i]备用。

这题的状态是一个二维数组dp[i][j]:将以i为根的树减少j个结点所需的最少步数。最开始,将dp[i][sum[i]]=1,dp[i][0]=0,其它的 dp[i][j]设为一个很大的数。之后是一个类似分组背包的过程:每一棵子树为一个组,在这棵子树上减少各结点数为组中的物品,减少的结点数为体积,减少所需步数为价值。之后就是分组背包的状态转移过程:对于每一棵子树,从大到小枚举减少的结点数,判断减少的结点数的步数是否能通过减少当前子树上的一个值变得更小。

由于我对于分组背包的理解非常粗浅,比赛的那天我已经确定了状态、边界值和状态转移方程,唯独这个状态转移的顺序想了四十多分钟没有结果,导致我以为不能以它作为状态,将一切推倒重来,但是兜兜转转又把状态选回这上面了。后面两天有空的时候一直会把这一题拿出来想,直到第三天凌晨睡觉的时候灵光一现。

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
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <cctype>
using namespace std;
struct ege
{
int to, nex;
} edge[305];
int head[155], sum[155], tot, dp[155][155], n, p;
void addedge(int from, int to)
{
edge[tot].nex = head[from];
edge[tot].to = to;
head[from] = tot++;
}
int countsum(int root, int pre)
{
sum[root] = 1;
for (int i = head[root]; i; i = edge[i].nex)
{
if (edge[i].to != pre)
sum[root] += countsum(edge[i].to, root);
}
return sum[root];
}
void init()
{
for (int i = 0; i <= n; i++)
sum[i] = 0;
for (int i = 0; i <= n; i++)
{
for (int j = 0; j <= n; j++)
dp[i][j] = 1e9;
}
}
void dfs(int root, int pre)
{
dp[root][0] = 0;
dp[root][sum[root]] = 1;
int i, j, k;
for (i = head[root]; i; i = edge[i].nex)
{
if (edge[i].to != pre)
{
dfs(edge[i].to, root);
for (j = sum[root]; j > 0; j--)
{
for (k = 1; k <= sum[edge[i].to] && k <= j; k++)
dp[root][j] = min(dp[root][j], dp[root][j - k] + dp[edge[i].to][k]);
}
}
}
}
int main()
{
while (~scanf("%d%d", &n, &p))
{
init();
tot = 1;
int i, j, u, v, res = 1e9;
for (i = 0; i <= n; i++)
head[i] = 0;
for (i = 1; i < n; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
for (i = 1; i <= n; i++)
{
init();
countsum(i, -1);
dfs(i, -1);
if (dp[i][n - p] < res)
res = dp[i][n - p];
}
printf("%d\n", res);
}
return 0;
}
页阅读量:  ・  站访问量:  ・  站访客数: