[CTSC1997] 选课

前言:

主播刚学树上依赖性背包,写点东西记录下。

题目描述:

在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有 \(N\) 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程 \(a\) 是课程 \(b\) 的先修课即只有学完了课程 \(a\),才能学习课程 \(b\))。一个学生要从这些课程里选择 \(M\) 门课程学习,问他能获得的最大学分是多少?

解题思路:

注意到如果一个点不选,那么他的子树一定不会选。又注意到一个点的子树的dfs序是一段连续的区间,那么从后往前递推可以得到以下转移。

\[\begin{equation} \begin{cases} f_{i,j} \gets f_{i+1,j-1} \\ f_{i,j} \gets f_{i+siz_{i},j} \\ \end{cases} \nonumber \end{equation} \]

时间复杂度:\(O(NM)\)

代码实现:

#include<bits/stdc++.h>
using namespace std;
const int N = 3e3 + 10;
vector<int> v[N];
int n, m, tot, f[N][N], val[N], dfn[N], siz[N], pos[N];
void dfs(int x, int fa){
    dfn[x] = ++tot, siz[x] = 1, pos[dfn[x]] = x;
    for(int y : v[x]){
        if(y == fa) continue;
        dfs(y, x);
        siz[x] += siz[y];
    }
}
int main(){
    cin >> n >> m;
    for(int i = 1, x; i <= n; i++){
        cin >> x >> val[i], pos[i] = i;
        v[x].push_back(i), v[i].push_back(x);
    }
    dfs(0, 0);
    for(int _ = tot; _ >= 2; _--){
        int i = pos[_];
        for(int j = 1; j <= m; j++){
            f[i][j] = max(f[i][j], f[pos[_ + 1]][j - 1] + val[i]);
            f[i][j] = max(f[i][j], f[pos[_ + siz[i]]][j]);
        }
    }
    cout << f[pos[2]][m] << endl;
    return 0;
}
posted @ 2025-07-19 14:51  _huangweiliang  阅读(12)  评论(0)    收藏  举报