[oiclass1454]选课:树上背包
题目
在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习。现在有 门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程 是课程 的先修课即只有学完了课程 ,才能学习课程 )。一个学生要从这些课程里选择 门课程学习,问他能获得的最大学分是多少?
输入
第一行有两个整数 用空格隔开。()
接下来的 行,第 行包含两个整数 和 , 表示第 门课的直接先修课, 表示第 门课的学分。若 表示没有直接先修课()。
输出
只有一行,选 门课程的最大得分。
输入样例
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
输出样例
13
题解
经典题,传统解法是将讲多叉树转化成二叉树,然后在二叉树上进行树形DP,现在使用树上背包来解会更简单。
需要事先学习树上背包,理解分组背包的求法,理解如何将问题转化成树上背包问题。
本题有两种定义方式。
1
定义表示以i为根选择j门课程的最大得分(包含i的结点),代码如下:
点击查看代码
#include<bits/stdc++.h> using namespace std; const int N=300+5; vector<int> g[N]; int n,m,u,v,w,f[N][N],s[N]; void dfs(int u){ for(int i=1;i<=m;i++)f[u][i]=s[u]; for(int i=0;i<g[u].size();i++){ int v=g[u][i]; dfs(v); for(int j=m;j>=0;j--){ for(int k=0;k<j;k++){ f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]); } } } } int main(){ scanf("%d %d",&n,&m); for(int i=1,x;i<=n;i++){ scanf("%d %d",&x,&s[i]); g[x].push_back(i); } m++; dfs(0); printf("%d",f[0][m]); }
2
定义表示以i为根选择j门课程的最大得分(不包含i的结点),代码如下:
点击查看代码
#include<iostream> #include<vector> using namespace std; vector<int> g[301]; int n,m,x,s[301],f[301][201]; void dfs(int u){ for(int i=0;i<g[u].size();i++){ int v=g[u][i]; dfs(v); for(int j=m;j>=0;j--){ for(int k=0;k<j;k++){ f[u][j]=max(f[u][j],f[u][j-k-1]+f[v][k]+s[v]); } } } } int main(){ cin>>n>>m; for(int i=1;i<=n;i++){ cin>>x>>s[i]; g[x].push_back(i); } dfs(0); cout<<f[0][m]; }
分类:
oi
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话