f[i][j]表示以i为根的树保留j个节点所需要剪断的最少的边数;
状态转移方程:f[i][j]=min( f[i.son][k]+f[i][j-k]-1,f[i][j] ) 之所以有减1是因为计算f[i][j-k]时是把i->i.son的边剪断了的。
#include<stdio.h>
#include<string.h>
#define min(x,y) ((x)<(y)?(x):(y))
typedef struct
{
int v,next;
}Node;Node node[310];
int vc[160],sum[160]; int p=1; int f[160][160]={0};int num[160]; //num记录每个节点的直接儿子的个数,sum记录以每个节点为根的子树中的节点数目
void addedge(int i,int j)
{
node[p].v=j;
node[p].next=vc[i];
vc[i]=p++;
num[i]++;
}
void dfs(int k)
{
int u,v,i,j,s;
u=vc[k]; sum[k]=1;
if(u==-1)
{
sum[k]=1; f[k][1]=0; return;
}
for(;u!=-1;u=node[u].next)
{
v=node[u].v;
dfs(v);
sum[k]+=sum[v];
for(j=sum[k];j>0;j--)
for(s=1;s<j;s++)
f[k][j]=min(f[k][j],f[k][j-s]+f[v][s]-1);
}
}
int main()
{
int N,P,i,j,k;
memset(vc,-1,sizeof(vc)); memset(num,0,sizeof(num));
memset(sum,0,sizeof(sum));
scanf("%d%d",&N,&P);
for(k=1;k<=N-1;k++)
{
scanf("%d%d",&i,&j);
addedge(i,j);
}
for(i=1;i<=N;i++) //初始化很重要
for(j=1;j<=N;j++)
f[i][j]=10000000000LL;
for(i=1;i<=N;i++)
f[i][1]=num[i];
dfs(1);
int ans;
for(i=1;i<=N;i++)
{
if(i==1) ans=f[1][P];
else
{
ans=min(ans,f[i][P]+1); // 加1是因为当选择非根节点时需要将它与它的父亲节点断开,所以加1
}
}
printf("%d",ans);
return 0;
}