LibreOJ 10154 选课

题目链接:LibreOJ 10154 选课

题目大意:

题解:
树上的背包问题。
不妨给所有没有先修课程的课增加先修课程(0),设(dp[i][j])表示以(i)作为根的子树修读(j)门课获得的最大学分。
状态转移方程:

[dp[u][i] = max{dp[u][i], dp[u][i - j] + dp[v][j]} ]

由于想要这棵子树上的学分,(u)为必修课程,所以还需要更新一遍(u)的学分。

[dp[u][i] = dp[u][i - 1] + score[u] ]

答案为(dp[0][m])

#include <cstring>
#include <iostream>
using namespace std;
#define N 110

int n, m, score[N], head[N], cnt, dp[N][N];
struct Edge {
    int v, next;
} edge[N];

void addEdge(int u, int v) {
    edge[++cnt].v = v;
    edge[cnt].next = head[u];
    head[u] = cnt;
}

void dfs(int u) {
    dp[u][0] = 0;
    for (int p = head[u]; ~p; p = edge[p].next) {
        int v = edge[p].v;
        dfs(v);
        for (int i = m; i >= 0; --i) {
            for (int j = 0; j <= i; ++j) {
                dp[u][i] = max(dp[u][i], dp[u][i - j] + dp[v][j]);
            }
        }
    }
    if (u) {
        for (int i = m; i >= 1; --i) {
            dp[u][i] = dp[u][i - 1] + score[u];
        }
    }
}

int main() {
    memset(head, -1, sizeof(head));
    cin >> n >> m;
    for (int i = 1, x; i <= n; ++i) {
        cin >> x >> score[i];
        addEdge(x, i);
    }
    dfs(0);
    cout << dp[0][m];
    return 0;
}
原文地址:https://www.cnblogs.com/IzumiSagiri/p/15068378.html