题目分析
树形dp经典题目,没做过此类题,想了好半天,用到了分组背包的思想,注释写的很清楚,因此就不多讲了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 155;
const int INF = 0x3f3f3f3f;
int head[maxn],fa[maxn],dp[maxn][maxn],tot,N,P;
struct Edge{
int to,next;
}e[maxn<<1];
void addedge(int from, int to){
e[tot].to = to;
e[tot].next = head[from];
head[from] = tot++;
}
void dfs(int u){
for(int i = 0; i <= P; i++)
dp[u][i] = INF;
dp[u][1] = 0; //最后递归到叶子节点需要该值
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to;
dfs(v);
for(int j = P; j >= 1; j--){ //分组背包的思想,因为需要在每一棵子树上选取一定的节点,所以每棵子树相当于一个分组,不理解可以先去看一下dd大牛的背包九讲
dp[u][j] = dp[u][j] + 1; //该出意思是直接把以v为根节点的子树去掉需要删除一条边,因此需要加1
for(int k = 1; k < j; k++)
dp[u][j] = min(dp[u][j], dp[u][k] + dp[v][j-k]);
}
}
}
int main(){
while(scanf("%d%d", &N, &P) != EOF){
int from,to;
tot = 0;
memset(fa, -1, sizeof(fa));
memset(head, -1, sizeof(head));
for(int i = 1; i < N; i++){
scanf("%d%d", &from, &to);
addedge(from, to);
fa[to] = from;
}
int root = 1;
while(fa[root] != -1){
root = fa[root];
}
dfs(root);
int ans = dp[root][P];
for(int i = 1; i <= N; i++)
ans = min(dp[i][P]+1, ans); //之所以要加1是因为子树的根要切断与父节点的联系,需要多切除一条边。
printf("%d\n", ans);
}
return 0;
}