poj 1947(树形DP)

poj 1947
之前不知道怎么规划状态……看了题解才知道……定义状态定义需要的就行了
设$dp(u, j)$为以$u$点为根的子树剩$j$个点的最小操作数
则初始化
$dp(u, 1)=|son \in u|(u$不是叶子)
$dp(u, 1)=0(u$是叶子)
转移简单了,$dp(u, j)=min(dp(u, j -k) + dp(v, k)-1)$
减一是因为之前$(u,v)$是断开的,把$(u,v)$连上就少一次操作

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
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
using namespace std;
const int MAXN = 150 + 5;
int n, p, f[MAXN][MAXN], fa[MAXN];
vector<int> G[MAXN];
void dp(int u, int pa) {
int flag = true;
fa[u] = pa;
f[u][1] = G[u].size() - (bool)(pa);
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (v != pa) {
flag = false;
dp(v, u);
for (int j = p; j > 1; j--) {
for (int k = 1; k < j; k++) {
f[u][j] = min(f[u][j], f[u][j - k] + f[v][k] - 1);
}
}
}
}
if (flag) {
f[u][1] = 0;
}
}
void clean() {
ms(f, 24);
for (int i = 0; i <= n; i++) fa[i] = 0, G[i].clear();
}
void solve() {
clean();
for (int u, v, i = 1; i < n; i++) {
scanf("%d%d", &u, &v);
G[u].push_back(v), G[v].push_back(u);
}
dp(1, 0);
int ans = 1000000000;
for (int i = 1; i <= n; i++) {
ans = min(ans, f[i][p] + (bool)(fa[i]));
}
printf("%d\n", ans);
}
int main() {
while (scanf("%d%d", &n, &p) == 2) solve();
return 0;
}

------ 本文结束 ------