Loading [MathJax]/jax/element/mml/optable/BasicLatin.js

『选课 有树形依赖的背包问题』

Parsnip·2019-01-29 13:42·445 次阅读

『选课 有树形依赖的背包问题』

『选课 有树形依赖的背包问题』

<更新提示>

<第一次更新>


<正文>

选课(tyvj 1051)#

Description#

学校实行学分制。每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。学校开设了N(N < 300)门的选修课程,每个学生可选课程的数量M是给定的。学生选修了这M门课并考核通过就能获得相应的学分。

在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。例如《Frontpage》必须在选修了《Windows操作基础》之后才能选修。我们称《Windows操作基础》是《Frontpage》的先修课。每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。每门课都有一个课号,依次为1,2,3,…。
你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修课优先的原则。假定课程之间不存在时间上的冲突。

Input Format#

输入文件的第一行包括两个整数N、M(中间用一个空格隔开)其中1≤N≤300,1≤M≤N。

以下N行每行代表一门课。课号依次为1,2,…,N。每行有两个数(用一个空格隔开),第一个数为这门课先修课的课号(若不存在先修课则该项为0),第二个数为这门课的学分。学分是不超过10的正整数。

Output Format#

输出文件只有一个数,实际所选课程的学分总数。

Sample Input#

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

Sample Output#

Copy
13

解析#

由题意可得,课程与课程之间的先修关系构成了一棵树,在最优化问题中,我们通常是用树形DP来求解的。那么我们可以先尝试树形DP的方法:设f[i][j]代表以i为根的子树中选了j门课程的最大学分数。显然,这个状态的转移在每颗子树中都可以选取若干门课程,那么,我们首先可以得出如下的状态转移方程:

f[i][j]=max

其中|son(i)|代表i的子节点个数,son_k代表其中某个子节点的编号,c_k则代表对于每一颗子树选取的课程门数,需满足:\sum_{k=1}^{|son(i)|}c_k=j-1
事实上,这是一个分组背包模型:j-1即为背包体积,每一个son_k即为一组物品,每组组物品分别有j-1个,每一个体积分别为c_k,价值为f[son_k][c_k],而每组物品我们只能从中选取至多一个,使得总价值最大。
那么对于每一颗子树,我们就可以用分组背包的方程来转移了。

f[i][j]=\max\{f[i][t-j]+f[son][j]\}

注意一个细节,没有先修课的课程,在输入中我们认为它的先修课是一个虚拟课程0,那么在i=0时,背包的体积为j,不需要为该门课程本身留下一个位置,所以我们可以将带有这门课程本身的状态额外的转移一次。

Code:

Copy
#include<bits/stdc++.h> using namespace std; const int N=300+30,M=300+30; int n,m,f[N][M],score[N]; vector < int >Link[N]; inline void input(void) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { int u,v; scanf("%d%d",&u,&v); Link[u].push_back(i); score[i]=v; } } inline void dp(int root) { f[root][0]=0; for(int i=0;i<Link[root].size();i++) { int Son=Link[root][i]; dp(Son); for(int v=m;v>0;v--) { for(int j=1;j<=v;j++) { f[root][v]=max(f[root][v],f[Son][j]+f[root][v-j]); } } } if(root!=0) for(int v=m;v>0;v--) f[root][v]=f[root][v-1]+score[root]; } int main(void) { freopen("test.in","r",stdin); freopen("test.out","w",stdout); input(); dp(0); printf("%d\n",f[0][m]); }

<后记>

posted @   Parsnip  阅读(445)  评论(0)    收藏  举报
编辑推荐:
· Hangfire Redis 实现秒级定时任务,使用 CQRS 实现动态执行代码
· Android编译时动态插入代码原理与实践
· 解锁.NET 9性能优化黑科技:从内存管理到Web性能的最全指南
· 通过一个DEMO理解MCP(模型上下文协议)的生命周期
· MySQL下200GB大表备份,利用传输表空间解决停服发版表备份问题
阅读排行:
· 工良出品 | 长文讲解 MCP 和案例实战
· 多年后再做Web开发,AI帮大忙
· 国产的 Java Solon v3.2.0 发布(央企信创的优选)
· centos停服,迁移centos7.3系统到新搭建的openEuler
· (原创)[开源][.Net Framework 4.0] SimpleLiveDataFeed(极易
点击右上角即可分享
微信分享提示
目录