树上背包学习笔记

树上背包学习笔记

做完洛谷P2014实在心绪澎湃,感觉对树上背包有点感触所以分享一下心得

以洛谷P2014为例

我们设状态dp[u][j][m]为以u为根节点,只从前j个子树中选m个点的最大学分

那么这个问题就和01背包很像了,不过对于每个子树(每个物品)还要枚举它可能的不同学分(价值

即在这个子树中选几个点

这么说更像是一个子树代表好几个物品

所以总的来说就是一个01背包了

那么就可以注意到滚动数组优化

第二维可以省略掉

状态转移方程:dp[u][j] = max(dp[u][j], dp[to[i]][k - 1] + v[i] + dp[u][j - k]);(k <- 1 to j)

j倒序枚举,k无所谓(物品出现先后顺序对问题无影响)

代码如下

#include <bits/stdc++.h>
using namespace std;
const int N = 310;
int h[N], to[N], v[N], ne[N], idx;
void add(int a, int b, int c) {
	to[++idx] = b;
	ne[idx] = h[a];
	h[a] = idx;
	v[idx] = c;
}
int dp[N][N];
void dfs(int u, int m) {
	if(m == 0) return;
	for(int i = h[u];i;i = ne[i]) {
		dfs(to[i], m - 1);
		for(int j = m;j;--j) {
			for(int k = j;k;--k) {
				dp[u][j] = max(dp[u][j], dp[to[i]][k - 1] + v[i] + dp[u][j - k]);
			}
		}
	}
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int n, m;
	cin>>n>>m;
	for(int i = 1;i <= n;++i) {
		int a, b;
		cin>>a>>b;
		add(a, i, b);
	}
	dfs(0, m);
	cout<<dp[0][m];
	return 0;
}
posted @ 2025-08-12 17:31  七月封阳  阅读(11)  评论(0)    收藏  举报