洛谷1273(树上分组背包)

要点

  • 扫每个儿子时更新一次dp
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

const int maxn = 3005;
int n, m, Cost[maxn];
int f[maxn][maxn], size[maxn];
vector<pair<int, int>> adj[maxn];

void dfs(int u) {
	if (u > n - m) {//叶子
		size[u] = 1;
		f[u][1] = Cost[u];
	}
	f[u][0] = 0;
	for (auto son : adj[u]) {
		int v = son.first, c = son.second;
		dfs(v);
		size[u] += size[v];
		for (int j = size[u]; j; j--) {//必须倒序,背包其实省略了一维[i]
			for (int k = 0; k <= min(j, size[v]); k++) {//当前子树选几个
				f[u][j] = max(f[u][j], f[v][k] + f[u][j - k] - c);
			}
		}
	}
}

int main() {
	scanf("%d %d", &n, &m);
	for (int u = 1, k, v, cost; u <= n - m; u++) {//建图
		for (scanf("%d", &k); k; k--) {
			scanf("%d %d", &v, &cost);
			adj[u].emplace_back(v, cost);
		}
	}
	for (int u = n - m + 1; u <= n; u++) {//叶子的值
		scanf("%d", &Cost[u]);
	}

	memset(f, 0xcf, sizeof f);//-inf
	dfs(1);
	for (int i = n; ~i; --i)
		if (f[1][i] >= 0) {//不亏本
			printf("%d\n", i);
			break;
		}
}
posted @ 2019-06-10 15:53  AlphaWA  阅读(183)  评论(0编辑  收藏  举报