树形dp,以深度为阶段,
dp[i][j]表示i结点为根的子树中,保留j个结点(必须保留i结点)最多保留多少边
天真的思路:总边数-最大保留=最小去掉
然而这样会把不需要的儿子和它的子子孙孙的连边都删了,,,
但其实只需要删除儿子和当前结点的连边,所以状态会与决策过程中从几个儿子转移过来有关系,需要加一维
dp[i][j]表示i结点为根的子树中,保留j个结点(必须保留i结点),i与i的父亲相连,最少删多少边
dp[i][0]=正无穷,dp[i][1]=儿子个数,枚举儿子,考虑是否要加上当前儿子并把删去的边数-1
统计结果时非根结点需要+1删去与父亲相连的边
代码
#include<bits/stdc++.h>
using namespace std;
int n,p,f[200],in[200],out[200],sum[200],nxt[200],minn=200,head[200],dp[200][200];
void build(int father,int son)
{
f[son]=father;
nxt[son]=head[father];
head[father]=son;
}
void dfs(int u)
{
dp[u][1]=out[u];
for(int i=head[u];i;i=nxt[i]) dfs(i);
for(int i=head[u];i;i=nxt[i])
for(int j=p;j>=1;j--)
{
for(int k=j;k>=1;k--)
dp[u][j]=min(dp[u][j],dp[i][k]+dp[u][j-k]-1);
}
if(in[u]) minn=min(minn,dp[u][p]+1);
else minn=min(minn,dp[u][p]);
}
int main()
{
memset(dp,0x3f,sizeof(dp));
cin>>n>>p;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
build(u,v);
in[v]++;
out[u]++;
}
for(int i=1;i<=n;i++)
{
if(in[i]==0)
{
dfs(i);
cout<<minn;
break;
}
}
return 0;
}
dp[i][j]表示i结点为根的子树中,保留j个结点(必须保留i结点),i不与i的父亲相连,最少删多少边
dp[i][1]=i的所有连边数
转移的时候要-2因为对于每条边儿子和父亲都减了一次