[CTSC1997] 选课 树上背包

[CTSC1997] 选课 树上背包

题意

选择一些课程,每个课程有一个唯一的先修课程,每个课程有一个学分,今有一名学生需要从中选择(m)门课程,问能够获得的最大学分是多少,每门课程的选择前提是选择先修课程

[1leq nleq 300\ 1leq mleq 300 \ k_i和s_i表示第i门课程的先修课程,s_i表示第i门课的学分 ]

分析

每个点有唯一的前驱,因此可以转化为树上问题

(dp[i][j])表示第(i)个结点选择了(j)个子孙结点能够取到的最大学分,这就把问题转化成了树上背包问题

[dp[i][j] = max(dp[i][j - k + 1] + dp[v][k]) ]

根据这个方程,在树上DFS一遍就可以了

注意细节,因为第二维不算自己,所以要注意+1-1

当然这里还有细节,刷新的时候要从大到小枚举,否则会影响后面的答案

代码

vector<int> e[505];
int dp[505][505];
int w[505];

int dfs(int u) {
    if (!e[u].size()) return 0;
    int siz = 0;
    for (auto v : e[u]) {
        int t = dfs(v);
        siz += t + 1;
        for (int j = siz; j >= 0; j--)
            for (int k = 0; k <= min(t,j - 1); k++)
                dp[u][j] = max(dp[u][j], dp[u][j - k - 1] + dp[v][k]);
    }
    return siz; 
}

int main() {
    int n = readint();
    int m = readint();
    for (int i = 1; i <= n; i++) {
        int x = readint();
        int y = readint();
        if (!x)  e[0].push_back(i);
        else e[x].push_back(i);
        w[i] = y;
    }
    for (int i = 1; i <= n; i++)
        dp[i][0] = w[i];
    dfs(0);
    cout << dp[0][m];
}
原文地址:https://www.cnblogs.com/hznumqf/p/13865745.html