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)$连上就少一次操作

#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;
}
------ 本文结束 ------