依然典型的dp背包问题;不同之处需要说明一下;
第一点状态转移方程原方程为
d[ i ][ j ] = min( d[ i ][ j ] , d[ i ][ j - k ] + d[ p ][ k-1 ] ); 其中的状态含义从根i出发i节点已经选取,还要在选取j个点的最小删除边代价; p为i节点的任一子节点;
要注意到形成的大小为m的子树,一定可以在给定的树上找到子树根节点,并且该子树与子树根节点联通;所以这是后来这样设计状态的依据;
若设d [ i ][ j ] 为从i出发i未选取, 还要在选取j个点的最小删除边代价;在状态转移时,状态转移式子为:
d[ i ][ j ] = min( d[ i ][ j ] , d[ i ][ j - k ] + d[ p ][ k ] ); 但注意k必须小于j,原因很简单,如果那样会导致i与该子节点不连通;
还有在dfs实现时候,第一开始认为该该根节点树没有子节点,然后逐步添加;
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int inf = 1000000;
const int maxn = 155;
int len,head[maxn];
struct node{
int to,next;
}tree[maxn*2];
void add_eadge(int u,int v){
tree[len].to = v;
tree[len].next = head[u];
head[u] = len++;
}
void init(){
memset(head,-1,sizeof(head));
len = 0;
}
int n,m,d[maxn][maxn],father[maxn],tem[maxn];
void dfs(int u,int f){
d[u][0] = 0;
for(int i=1;i<=m;i++) d[u][i]=inf;
for(int i=head[u];i!=-1;i=tree[i].next){
if(tree[i].to==f) continue;
int p = tree[i].to;
dfs(p,u);
for(int j=0;j<=m;j++) tem[j] = d[u][j];
for(int j=0;j<=m;j++){
d[u][j]=tem[j]+1;
for(int k=1;k<=j;k++)
d[u][j]=min(d[u][j],tem[j-k]+d[p][k-1]);
}
}
}
int main()
{
while(scanf("%d %d",&n,&m)==2){
init();
memset(father,0,sizeof(father));
for(int i=1;i<n;i++){
int x,y;
scanf("%d %d",&x,&y);
add_eadge(x,y);
father[y]=1;
}
int root;
for(int i=1;i<=n;i++){
if(!father[i]){
root = i;
dfs(i,-1); break;
}
}
int ans = d[root][m-1];
for(int i=1;i<=n;i++)
ans=min(ans,d[i][m-1]+1);
printf("%d\n",ans);
}
return 0;
}