树形dp!!!关键是枚举s点的所有的不去掉k子树的情况!!!
因为是树形DP,而子问题往往涉及的是子树的信息,所以我们要对树先进行深搜,求出子问题的解,然后再返回根节点,求根节点的信息。。。
#include<iostream>
#include<stdio.h>
#include<string.h>
#define maxn 155
using namespace std;
int n,p;
int son[maxn],blo[maxn],hf[maxn];//son[i]为i的儿子blo[i]为i的兄弟,hf[i]表示i是否有父亲
int dp[maxn][maxn];//dp[i][j]表示以i为根得到j个点的子树(不包括根)所要删除的边的条数;
void dfs(int s){
int i,j,k,temp;
for(i=0;i<=p;i++) dp[s][i]=0xffff;
dp[s][1]=0;
k=son[s];
while(k){//对于每一,如果去掉k子树,则dp[s][i]=dp[s][i]+1;
//否者为dp[s][i]=min(min(dp[s][j]+dp[k][i-j]))这里就是双重dp!!!
dfs(k);
for(i=p;i>=0;i--){
temp=dp[s][i]+1;
for(j=0;j<=i;j++){
if(dp[k][i-j]+dp[s][j]<temp)
temp=dp[k][i-j]+dp[s][j];}
dp[s][i]=temp;
}
k=blo[k];//根据他来确定其他点父亲
}
}
int solve(int root){
dfs(root);
int i,ans;
ans=dp[root][p];
for(i=1;i<=n;i++){
if(dp[i][p]<ans)
ans=dp[i][p]+1;//去掉子树与根i的边
}
return ans;
}
int main(){
while(scanf("%d%d",&n,&p)!=EOF){
memset(hf,0,sizeof(hf));
memset(blo,0,sizeof(blo));
memset(son,0,sizeof(son));
int a,b;
for(int i=0;i<n-1;i++){
cin>>a>>b;
blo[b]=son[a];
son[a]=b;
hf[b]=1;
}
int root;
for(int i=1;i<=n;i++) if(!hf[i]) root=i;//没有父节点的是根;
cout<<solve(root)<<endl;
}
return 0;
}