树型分组背包(模板题)

感觉还是非常巧妙地

光说理论不好讲,拿道题目来说P1273 有线电视网

这道题很难想

, 但是如果把每棵子树当成一个组,那么组内可以不选叶子节点

1 , 2 . . . . . . . 选1个叶子节点,2个叶子节点.......

而一个组中最多只能有一种决策,这不就是分组背包吗?

\color{Red}回忆一下普通的分组背包转移

for(每一组物品)
for(背包的容量)
for(当前这组选哪些物品)
	开始dp

仿 , d p [ i ] [ u ] [ j ] 那我们效仿这个,定义状态dp[i][u][j]的含义为

u , i j 在u为根的子树中,在前i个儿子中选择j个叶子节点的最大收益

其中儿子代表每组物品,要选的叶子节点数是容量,当前儿子选几个叶子节点是这组要选哪些物品

然后的话把背包容量倒序枚举可以优化掉一维

#include <bits/stdc++.h>
using namespace std;
const int maxn=3009;
int n,m,dp[maxn][maxn],w[maxn];
struct p{
	int to,nxt,w;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int w){
	d[cnt]=(p){ v,head[u],w},head[u]=cnt++;
}
int dfs(int u)
{
	dp[u][0]=0;
	if( u>n-m )
	{
		dp[u][1]=w[u];
		return 1;
	}
	int leaf=0;
	for(int i=head[u];i;i=d[i].nxt)
	{
		int v=d[i].to;
		int t=dfs(v);
		leaf+=t;
		for(int j=leaf;j>0;j--)//一共选几个叶子节点 
		{
			for(int q=0;q<=j&&q<=t;q++)
				dp[u][j]=max(dp[u][j],dp[v][q]+dp[u][j-q]-d[i].w);
		}
	}
	return leaf;
}
int main()
{
	cin >> n >> m;
	for(int i=1;i<=n-m;i++)
	{
		int s; cin >> s;
		for(int j=1;j<=s;j++)
		{
			int r,w;
			cin >> r >> w;
			add(i,r,w);
		}
	}
	for(int i=0;i<=3000;i++)
	for(int j=0;j<=3000;j++)
		dp[i][j]=-999999999;	
	for(int i=n-m+1;i<=n;i++)	cin >> w[i];
	dfs(1); 
	for(int j=m;j>=0;j--)
	{
		if(dp[1][j]>=0)
		{
			cout<<j;
			return 0;
		}
	}
}

猜你喜欢

转载自blog.csdn.net/jziwjxjd/article/details/107375819