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;
}

浙公网安备 33010602011771号