题目大意:给出一棵树,让你隔断一些边,使得割完后剩下的联通块里有某个联通块刚好包含k个节点,然后隔断的边尽量少
也是一道典型的树形dp
然后很明显能看出要以每个节点i及其子树中保留j个节点最少割多少次来作为状态
这个题大概难点在初始化吧。。想了好久。。。
我当时的初始化是f[i][1]=vec[i].size()-1,也就是i节点的总儿子数
然后转移分两种
一种是选了某个儿子那就是now=min(now,f[u][j-k]+f[tmp][k]-1);因为我一开始初始化的时候是默认都断开的,所以我现在必须把多删的这一条边减去
第二种就是不选某个儿子f[u][j]=min(now,f[u][j]);
写滚动数组不知道会不会超时反正拿个now来更新不会有问题。。。
最后答案要统计每一个节点的f[i][k]。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<vector>
using namespace std;
int f[210][210],n,p;
int ans=2000000000;
vector <int> vec[210];
void dfs(int u,int v)
{
f[u][1]=vec[u].size()-1;
if (v==0) f[u][1]++;
for (int i=vec[u].size()-1;i>=0;i--)
{
int tmp=vec[u][i];
if (tmp==v) continue;
dfs(tmp,u);
for (int j=p;j;j--)
{
int now=f[u][j];
for (int k=1;k<=j-1;k++)
now=min(now,f[u][j-k]+f[tmp][k]-1);
f[u][j]=min(now,f[u][j]);
}
}
if (v!=0) ans=min(ans,f[u][p]+1);
else ans=min(ans,f[u][p]);
}
int main()
{
int x,y;
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
scanf("%d%d",&n,&p);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
f[i][j]=1000000000;
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
vec[x].push_back(y);
vec[y].push_back(x);
}
dfs(1,0);
printf("%d",ans);
return 0;
}