传送门
给出n个节点的一棵树,询问最少毁掉几条边才可以使一颗节点数为p的子树独立出来
分析:
感觉是树形DP(⊙o⊙)…
那么看看状态吧
f[i][j]代表使以i为根节点数为p的子树独立出来最少毁掉几条边>o< >_<
怎么转移呢 ?_?
首先,当我们dfs(root)时,还没有dfs任何一个子节点时,f[root][1]=0,这是显然的,以为当前这棵树只有root这一个节点
当我们dfs完第一个子节点时,f[root][1]++,这也是显然的(^__^) ……~~
然后,我们可以用to[i]和root的f已知数组来转移
f[root][j+k]=min(temp[j]+f[to[i]][k])
temp[j]是在未进行转移时的f[root][j],因为有可能用飞、当前子节点来更新还不如直接割掉当前子节点更优
(因为我是用-1来代表f数组未被更新过,以此来记录j和k枚举到哪里,所以要记录一下原始f数组,也可以记录son[root]来记录jk枚举到哪里,然后按照01背包倒着跑,就不用记录原始状态了O(∩_∩)O~~~~)
然后dfs完第二个子节点,第三个子节点……也是一样的道理,但是不同的是就不只是f[root][1]++了,而是每一个已经被更新的f[root][j]++
代码如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define MIN(i,j) (i<j?i:j)
using namespace std;
const int maxn=150+5;
int n,p,cnt,hd[maxn],to[maxn],nxt[maxn],f[maxn][maxn];
void add(int x,int y){
to[cnt]=y;
nxt[cnt]=hd[x];
hd[x]=cnt++;
}
void dfs(int root){
f[root][1]=0;
for(int i=hd[root];i!=-1;i=nxt[i]){
dfs(to[i]);
int temp[maxn];
memset(temp,-1,sizeof(temp));
for(int j=1;f[root][j]!=-1;j++)
temp[j]=f[root][j];
for(int j=1;f[root][j]!=-1;j++)
f[root][j]++;
for(int j=1;temp[j]!=-1;j++)
for(int k=1;f[to[i]][k]!=-1;k++)
f[root][j+k]=min(f[root][j+k]==-1 ? 0x3f3f3f3f : f[root][j+k],temp[j]+f[to[i]][k]);
}
}
int main(){
scanf("%d%d",&n,&p),cnt=0;
memset(hd,-1,sizeof(hd));
for(int i=1,x,y;i<n;i++){
cin>>x>>y;
if(x>y) swap(x,y);
add(x,y);
}
memset(f,-1,sizeof(f));
dfs(1);
int ans=0x3f3f3f3f;
for(int i=1;i<=n;i++)
if(f[i][p]!=-1)
ans=MIN(f[i][p]+(i!=1),ans);
cout<<ans<<endl;
return 0;
}
感谢YOUSIKI O(∩_∩)O~~
by >o< neighthorn