题目描述
从一棵树上选择数量最少的边断开 使得拆出的子树大小为P
题解
树上dp
设 (f[i][j]) 表示子树i中拆分出j个节点 需要最少删去多少边
考虑树上背包的模板 我们对于一个节点now
合并时候 把该节点的每个子树看成一个组 子树中每一个点看成物品进行分组背包
由于每个非根节点想要单独拆分出来,所以答案要+1
最后,由于拆分并不需要在根节点进行
所以对于每个节点都可以看一看有没有更优秀的答案
code:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
#define maxn 159
int n,p;
int f[maxn][maxn];
vector<int> son[maxn];
int dfs(int now,int fa)//返回子树个数
{
f[now][1]=0;
int size=1;
for(int i=0;i<son[now].size();i++)
{
int to=son[now][i];
if(to==fa)continue;
int tk=dfs(to,now);
size+=tk;
for(int j=size;j>=0;j--)//分j组
{
f[now][j]++;
for(int k=0;k<=min(tk,j-1);k++)//j-1强制选根
{
f[now][j]=min(f[now][j],f[to][k]+f[now][j-k]);
}
}
}
return size;
}
signed main()
{
scanf("%d%d",&n,&p);
memset(f,0x3f,sizeof(f));
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
son[x].push_back(y);
son[y].push_back(x);
}
dfs(1,0);
int ans=f[1][p];
for(int i=2;i<=n;i++)ans=min(ans,f[i][p]+1);
printf("%d
",ans);
return 0;
}
/*
f[i][j]表示子树i中分离出j个节点需要多少道路
强制当前子树i和父节点相连
f[i][j]变成了 有s个物品 每个节点有
*/
``