依然典型的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; }