1. 程式人生 > 其它 >P1272 重建道路 | 樹上揹包

P1272 重建道路 | 樹上揹包

P1272 重建道路 | 樹上揹包 題目描述 從一棵樹上選擇數量最少的邊斷開 使得拆出的子樹大小為P

題目描述

從一棵樹上選擇數量最少的邊斷開 使得拆出的子樹大小為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個物品 每個節點有 
*/
``