P1272 重建道路 | 树上背包

时间:2021-09-16
本文章向大家介绍P1272 重建道路 | 树上背包,主要包括P1272 重建道路 | 树上背包使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目描述

从一棵树上选择数量最少的边断开 使得拆出的子树大小为P

题解

树上dp
\(f[i][j]\) 表示子树i中拆分出j个节点 需要最少删去多少边
考虑树上背包的模板 我们对于一个节点now
合并时候 把该节点的每个子树看成一个组 子树中每一个点看成物品进行分组背包
由于每个非根节点想要单独拆分出来,所以答案要+1
最后,由于拆分并不需要在根节点进行
所以对于每个节点都可以看一看有没有更优秀的答案
code:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
#define maxn 159
int n,p;
int f[maxn][maxn];
vector<int> son[maxn];
int dfs(int now,int fa)//返回子树个数 
{
	f[now][1]=0;
	int size=1;
	for(int i=0;i<son[now].size();i++)
	{
		int to=son[now][i];
		if(to==fa)continue;
		int tk=dfs(to,now);
		size+=tk;
		for(int j=size;j>=0;j--)//分j组 
		{
			f[now][j]++;
			for(int k=0;k<=min(tk,j-1);k++)//j-1强制选根 
			{
				f[now][j]=min(f[now][j],f[to][k]+f[now][j-k]);
			}
		}
	}
	return size;
}
signed main()
{
	scanf("%d%d",&n,&p);
	memset(f,0x3f,sizeof(f));
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		son[x].push_back(y);
		son[y].push_back(x);
	}
	dfs(1,0);
	int ans=f[1][p];
	for(int i=2;i<=n;i++)ans=min(ans,f[i][p]+1);
	printf("%d\n",ans);
	return 0;
}
/*
f[i][j]表示子树i中分离出j个节点需要多少道路 
强制当前子树i和父节点相连 
f[i][j]变成了 有s个物品 每个节点有 
*/
``

原文地址:https://www.cnblogs.com/lzy-blog/p/15294779.html