题目大意
给定一棵n个结点的树,问最少需要删除多少条边使得某棵子树的结点个数为p
题解
很经典的树形DP~~~直接上方程吧 dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]-1) 方程的意思是 以u结点为根保留j个结点需要删除的最少的边的条数,那么可以选择在某个子结点v中选择k个保留,其他结点保留j-k个,为什么需要-1呢,因为相当于把子树v衔接到结点u上,因此边u->v是不需要删除的,所以要-1
代码:
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; #define MAXN 155 #define INF 0x3f3f3f3f struct node { int v,next; }; node edge[MAXN]; int head[MAXN],dp[MAXN][MAXN],p; void add_edge(int u,int v,int &k) { edge[k].v=v; edge[k].next=head[u]; head[u]=k++; } void dfs(int u) { if(head[u]==-1) { dp[u][1]=0; return; } int cnt=0; for(int i=head[u]; i!=-1; i=edge[i].next) { int v=edge[i].v; dfs(v); cnt++; } dp[u][1]=cnt; for(int i=head[u]; i!=-1; i=edge[i].next) { int v=edge[i].v; for(int j=p; j>=2; j--) for(int k=1; k<=j; k++) dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]-1); } } int main() { int n; while(scanf("%d%d",&n,&p)!=EOF) { int k=1; memset(head,-1,sizeof(head)); memset(dp,INF,sizeof(dp)); for(int i=1; i<n; i++) { int u,v; scanf("%d%d",&u,&v); add_edge(u,v,k); } dfs(1); int ans=dp[1][p]; for(int i=2; i<=n; i++) ans=min(ans,dp[i][p]+1); printf("%d\n",ans); } return 0; }