题目大意:给定一棵树,问最少删去几条边能使得有一部分被分离出来的子树的节点个树为k的;
题目解析:定义dp[i][j]为当前在第i个节点使得以他为根的子树有j的节点所删掉最少的边树,那么枚举他的所有儿子的时候,如果不要这个子树那么dp[root][j]就需要+1,否则就是个01背包了;最后如果根不是整棵树的根的时候还需要+1,因为需要把它从它的父亲节点断掉;
AC代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
using namespace std;
const int inf=0x3fffffff;
const int maxn=160;
int tot,head[maxn],n,m,dp[maxn][maxn],sum[maxn];
bool vis[maxn];
struct Edge
{
int to,next;
}edge[maxn<<1];
void init()
{
memset(head,-1,sizeof(head));
tot=0;
}
void addedge(int u,int v)
{
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void dfs(int root,int fa)
{
dp[root][1]=0;
for(int i=head[root];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(fa==v) continue;
dfs(v,root);
for(int j=m;j>=1;j--)
{
int temp=dp[root][j]+1;
for(int k=1;k<j;k++)
{
if(dp[root][k]>=inf||dp[v][j-k]>=inf) continue;
temp=min(temp,dp[root][k]+dp[v][j-k]);
}
dp[root][j]=temp;
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
init();
memset(vis,0,sizeof(vis));
memset(sum,0,sizeof(sum));
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
dp[i][j]=inf;
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
vis[v]=1;
}
int root;
for(int i=1;i<=n;i++)
if(!vis[i])
root=i;
dfs(root,-1);
int ans=dp[root][m];
for(int i=1;i<=n;i++)
{
ans=min(ans,dp[i][m]+1);
}
printf("%d\n",ans);
}
return 0;
}