[SMOJ1793]选课
来源:互联网 发布:充值软件下载 编辑:程序博客网 时间:2024/05/22 12:49
题目描述
学校实行学分制。每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。学校开设了
N (N<300 )门的选修课程,每个学生可选课程的数量M 是给定的。学生选修了这M 门课并考核通过就能获得相应的学分。在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。例如《Frontpage》必须在选修了《Windows操作基础》之后才能选修。我们称《Windows操作基础》是《Frontpage》的先修课。每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。每门课都有一个课号,依次为 1,2,3,…。 例如:
表中 1 是 2 的先修课,2 是 3、4 的先修课。如果要选 3,那么 1 和 2 都一定已被选修过。 你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修课优先的原则。假定课程之间不存在时间上的冲突。
输入格式 1793.in
输入文件的第一行包括两个整数
N 、M (中间用一个空格隔开)其中1≤N≤300 ,1≤M≤N 。以下
N 行每行代表一门课。课号依次为1,2,…,N 。每行有两个数(用一个空格隔开),第一个数为这门课先修课的课号(若不存在先修课则该项为 0),第二个数为这门课的学分。学分是不超过 10 的正整数。
输出格式 1793.out
输出文件每行只有一个数。第一行是实际所选课程的学分总数。
输入样例 1793.in
输入样例一:
5 4
0 1
1 1
2 3
0 3
2 4
输入样例二:
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
输出样例 1793.out
输出样例一:
9
输出样例二:
13
这道题是树型 DP + 背包,也属于比较经典的题目。
题目大意:有一片森林(若干棵树),共
我们不妨简化一下题目,不考虑森林,也不考虑多叉,先从一棵二叉树的情况入手。
如图是一棵二叉树,我们要从中选一些点。如果我们要选 2 或 3,则必须先选 1,其他的类似。
不妨定义
首先,对于所有的
而对于每一个
例如,在上面的图中,
于是对于每一个问题都分别递归求解子问题,再枚举合并一下就好了,边界条件则是对于所有的叶子结点,
时间复杂度为
再来看一棵多叉树的情况。
我们不妨从左到右依次考虑每个子问题。例如对于以 1 为根结点时,要选
显然 1 的儿子们总共要选
怎么办呢?
枚举一下就好了。
既然我们是从左到右依次考虑的,那么例如我们在考虑 3 的时候,已经充分考虑过以 2 为根的子树对 1 的影响,也就是说整棵树可以大致分为两部分:
于是我们就可以枚举在以 3 为根的子树中取多少个点,然后它去改进已考虑。
写成状态转移方程就是
其中
其实此题跟“修复道路”是有相似之处的,都属于在树上做背包的情形。于是同理,也可以通过从大到小枚举
但是不要忘了,本题是森林而不是一棵多叉树。虽然分开求解最后再枚举合并也可以,但是更巧妙的方法则是加一个点 0,作为森林中每棵树的根结点的父亲,它的权值为 0,而所选点数要加上 1,即可。这样就将森林变成了一棵多叉树。
尽管是多叉,时间复杂度仍然是
参考代码:
#include <algorithm>#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>using namespace std;const int maxn = 1e3;int n, m;int score[maxn];int head[maxn];struct Edge { int to, next; } edge[maxn];int cnt = 0;void addEdge(int u, int v) { edge[++cnt].to = v; edge[cnt].next = head[u]; head[u] = cnt;}int dp[maxn][maxn];void dfs(int root, int pre) { dp[root][0] = 0; for (int i = 1; i <= m; i++) dp[root][i] = score[root]; for (int i = head[root]; i; i = edge[i].next) if (edge[i].to != pre) { dfs(edge[i].to, root); for (int j = m; j > 1; j--) for (int k = 1; k < j; k++) dp[root][j] = max(dp[root][j], dp[edge[i].to][k] + dp[root][j - k]); }}int main(void) { freopen("1793.in", "r", stdin); freopen("1793.out", "w", stdout); scanf("%d%d", &n, &m); ++m; for (int i = 1; i <= n; i++) { int father; scanf("%d%d", &father, &score[i]); addEdge(father, i); } dfs(0, 0); printf("%d\n", dp[0][m]); return 0;}
- [SMOJ1793]选课
- 选课
- 选课
- 选课
- 选课
- 选课
- 选课
- 选课
- 选课
- 选课
- 选课
- 选课了,选课了。
- 关于选课
- xmu1073 选课
- 选课纠结
- wikioi 选课
- 选课_DP
- tyvj1051选课
- 紫书例题6-5 UVa 12657 (链表
- JS获取当前日期前7天
- Linux sar 分析网卡流量
- 使用Sublime Text 3 搭建TeXlive 2016
- 1017. Queueing at Bank (25)
- [SMOJ1793]选课
- Linux命令行
- 自己动手实现简单权限控制
- 设备的使用,软硬连接,文件查找
- 十分钟看懂图像语义分割技术
- mybatis把整数0识别为null
- Ocr引擎
- 你喜欢的搞笑内容都在这里-----笑口常开
- mysql优化 索引