【LuoGu】2014 选课——树上DP

[CTSC1997] 选课

题目描述

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

输入格式

第一行有两个整数 N , M 用空格隔开。( 1N300 , 1M300 )

接下来的 N 行,第 I+1 行包含两个整数 kisi, ki 表示第I门课的直接先修课,si 表示第I门课的学分。若 ki=0 表示没有直接先修课(1kiN , 1si20)。

输出格式

只有一行,选 M 门课程的最大得分。

样例 #1

样例输入 #1

7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2

样例输出 #1

13

解决方案

树形DP

根据题意我们可以发现题目中给的图是一个森林,直接进行DP不太好处理。因此要使用一个技巧:超级结点
超级结点就是新建一个结点将所有连通块连接在一起使森林连接成一棵树。然后从超级结点出发就可以遍历整棵树了。
建立超级结点后就可以来考虑动态规划了。

f[i][j]表示在以i为根结点的子树上选取j个结点所能获得的最大分数。考虑结点ki,如果我们不从以k为根结点的子树上选结点的话,那么f[i][j]=f[i][j],如果我们从以k为根结点的子树上选择cnt个子结点的话,f[i][j]就由两部分组成:从子树k中选出的cnt个结点,以及从i的其他子树中选出来的jk个结点,状态转移方程为f[i][j]=f[i][jcnt]+f[k][cnt]。因此总的状态转移方程为f[i][j]=maxkson[i](f[i][j],f[i][jcnt]+f[k][cnt]),cnt[0,m]

#include<bits/stdc++.h>
const int N = 330;
int n, m;
std::vector<std::vector<int>> g(N);
int w[N];
int f[N][N];
void dfs(int u){
for(auto v:g[u]){
dfs(v);
for(int j = m + 1; j >= 0; j -- ){
for(int k = 0; k < j; k ++ ){
f[u][j] = std::max(f[u][j], f[u][j - k] + f[v][k]);
}
}
}
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cin >> n >> m;
for(int i = 1; i <= n; i ++ ){
int k, s;
std::cin >> k >> s;
g[k].push_back(i);
w[i] = s;
f[i][1] = s;
}
dfs(0);
std::cout << f[0][m + 1];
return 0;
}

题解参考:https://www.cnblogs.com/fusiwei/p/13753292.html


__EOF__

本文作者天涯海角寻天涯
本文链接https://www.cnblogs.com/yjx-7355608/p/17690107.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   天涯海角寻天涯  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示