P2014 [CTSC1997] 选课

链接

https://www.luogu.com.cn/problem/P2014

思路

树形dp。 要点:
  • dfs给所有节点重编号
  • dp[i][j]为:以第i个子节点为根节点时 选j个它的子节点(不是子树节点)的最大学分f[i][j]=max/min(f[i][j],f[i][j-k]+f[儿子节点编号][k]=....
  • f[i][j]表示第i个节点取j个子节点(不包括自己)所取得的最大(最小)收益。
  • 当然f[i][j]的第二维j要根据题目来定,像1273的第二维用来存接受端的数量比较好。
  • 然后思考一下就会发现,必须要先从儿子节点开始dp,那么第一步就是重要的。

dfs序原理:

附:

代码

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<string.h>
#include<string>
#include<vector>
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
#define int long long
int n, m;
const int N = 500;
vector<int>nxt[N];
int val[N];
int dp[N][N];
bool vis[N];
int sz[N];
void dfs(int i)
{
	if (vis[i])return;
	vis[i] = 1;
	if (nxt[i].size() == 0)
	{
		dp[i][1] = val[i];
		sz[i]++;
	}
	else
	{
		for (int j : nxt[i]) 
		{
			dfs(j);
			sz[i] += sz[j];
		}
		dp[i][1] = val[i];
		sz[i]++;
		for (int t : nxt[i])
		{
			for (int j = min(m,sz[i]); j > 1; j--)
			{
				for (int k = min(j-1, sz[t]); k > 0; k--)
					dp[i][j] = max(dp[i][j], dp[i][j - k] + dp[t][k]);
			}
		}/**/
	}
}

signed main()
{
	IOS;
	cin >> n >> m; m++;
	for (int i = 1; i <= n; i++)
	{
		int num, v;
		cin >> num >> v;
		nxt[num].push_back(i);
		val[i] = v;
	}
	int ans = 0;
	for (int i = n; i >= 0; i--)
	{
		dfs(i);
	}
	ans = dp[0][m];
	cout << ans;

	return 0;
}

posted @ 2025-03-07 09:38  WHUStar  阅读(48)  评论(0)    收藏  举报