题目来源:https://www.luogu.org/problemnew/show/P2014#sub
题意:给你n门课程,每门课有其先修课和学分。求选m门课程的最大学分。(森林转二叉树)
树形dp。关键是存树的方式。
在森林上找多个包含树根的连通块,使所有点的权值最大。
用二叉树存储,定义两个数组head和next,其中head[i]表示节点i的第一个儿子节点,next[i]表示节点i的兄弟节点。
若以知一个节点的父亲,则插入该节点的代码:
if (head[fa] == 0)head[fa] = i;
else {
int t = head[fa];
while (next[t] != 0)t = next[t];
next[t] = i;
}
dp方程:
f[c][s]表示在二叉树中以c为根的子树中取s个节点的最大权值,其中这s个节点均联通。
f[c][s]=a[c]+max(f[head[c]][i],f[next[c]][s-1-i]);
由于二叉树中节点c的右节点实际上与c是兄弟关系,故f[c][s]的值可以不包含节点c,故还应保证
f[c][s]=max(f[c][s],f[next[c]][s]);
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
int a[1001];
int head[1001], nxt[1001];
int f[1001][1001];
int dp(int c, int s) {
if (s == 0)return 0;
if (c == 0)return 0;
if (f[c][s] != 0)return f[c][s];
for (int i = 0; i < s; i++)f[c][s] = max(f[c][s], a[c] + dp(head[c], i) + dp(nxt[c], s - 1 - i));
f[c][s] = max(f[c][s], dp(nxt[c], s));
return f[c][s];
}
int main() {
__;
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
int fa;
cin >> fa;
if (head[fa] == 0)head[fa] = i;
else {
int t = head[fa];
while (nxt[t] != 0)t = nxt[t];
nxt[t] = i;
}
cin >> a[i];
}
cout << dp(head[0], m);
return 0;
}