一道接近模板的树形背包,难点在于割边是考虑返祖边的问题
设 f [ i ] [ j ] f[i][j] f[i][j] 为在以 i i i 为根的子树中要割出 j j j 个节点的子树,最少需要割掉多少条边。注意这个割出来的子树一定要以 i i i 为根
初始化
f
[
x
]
[
1
]
f[x][1]
f[x][1] 为
x
x
x 的度数
转移方程为:
f
[
x
]
[
j
]
=
m
i
n
(
f
[
x
]
[
j
]
,
f
[
x
]
[
j
−
k
]
+
f
[
y
]
[
k
]
−
2
)
f[x][j]=min(f[x][j],f[x][j-k]+f[y][k]-2)
f[x][j]=min(f[x][j],f[x][j−k]+f[y][k]−2)
这里的
−
2
-2
−2,由于初始化时一个点与它的父亲和儿子都是断开的,因此
[
u
,
v
]
[u,v]
[u,v] 和
[
v
,
u
]
[v,u]
[v,u] 这两条边都被断掉了。但在转移方程中需满足
x
,
y
x,y
x,y 连通,所以要
−
2
-2
−2
完整代码
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
const int Maxn=200,inf=0x3f3f3f3f;
int f[Maxn][Maxn];
bool ind[Maxn];
vector <int> e[Maxn];
int n,m,ans=inf,root;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
void dfs(int x)
{
f[x][1]=e[x].size()+1;
for(int i=0;i<e[x].size();++i)
{
int y=e[x][i];
dfs(y);
for(int j=m;j>1;--j)
for(int k=j-1;k;--k)
f[x][j]=min(f[x][j],f[x][j-k]+f[y][k]-2);
}
if(x==root)--f[x][m];
ans=min(ans,f[x][m]);
}
int main()
{
// freopen("in.txt","r",stdin);
n=read(),m=read();
for(int i=1;i<n;++i)
{
int x=read(),y=read();
e[x].push_back(y);
ind[y]=1;
}
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
f[i][j]=inf;
for(int i=1;i<=n;++i)
if(!ind[i]){root=i;break;}
dfs(root);
printf("%d\n",ans);
return 0;
}