重建道路

题目链接

树形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因为对于每条边儿子和父亲都减了一次

原文地址:https://www.cnblogs.com/qwq-/p/13680135.html