chenfy27的刷题记录

导航

luoguP2014 选课

有N门课程,每门课程有0或1门先修课程,课程i的学分为s[i],问选M门课能取得的最大学分是多少?
1<=N<=300; 1<=M<=300; 1<=s[i]<=20

分析:树上01背包,记dp[x][j]表示以x为根的子树最多选j门课的最大学分。对于节点x,如果要选其子节点,则必须选择x。枚举以子节点为根的子树修几门课,进行递推。

#include <bits/stdc++.h>
using i64 = long long;

void solve() {
	int N, M;
	std::cin >> N >> M;
	std::vector<int> w(N + 1);
	std::vector<std::vector<int>> adj(N + 1);
	for (int i = 1; i <= N; i++) {
		int x, y;
		std::cin >> x >> y;
		adj[x].push_back(i);
		adj[i].push_back(x);
		w[i] = y;
	}

	std::vector<std::vector<int>> dp(N + 1, std::vector<int>(M + 2));

	auto dfs = [&](auto self, int x, int p) -> void {
		for (int i = 1; i <= M + 1; i++) {
			dp[x][i] = w[x];
		}
		for (auto i : adj[x]) if (i != p) {
			self(self, i, x);
			for (int j = M + 1; j >= 1; j--) {
				for (int k = 0; k < j; k++) {
					dp[x][j] = std::max(dp[x][j], dp[i][k] + dp[x][j - k]);
				}
			}
		}
	};

	dfs(dfs, 0, 0);
	std::cout << dp[0][M + 1] << "\n";
}

int main() {
	std::cin.tie(0)->sync_with_stdio(0);
	int t = 1;
	while (t--) solve();
	return 0;
}

posted on 2024-11-03 15:50  chenfy27  阅读(2)  评论(0编辑  收藏  举报