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

浙公网安备 33010602011771号