【洛谷P2014】选课

题目描述

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

输入输出格式

输入格式:

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

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

输出格式:

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

输入输出样例

输入样例#1

7  4

2  2

0  1

0  4

2  1

7  1

7  6

2  2

输出样例#1

13

算法:

树形DP

 

分析:

这道题一拿起来就感觉它好像是一个背包问题。但其实普通的有依赖性背包问题是无法解决这种有互相依赖的像图一样的问题,然而我们又发现先修课和后续课存在着关联,于是我们就可以用树形DP。

 

假如按照之前套路走,我们应该构建一棵多叉树,在这棵树上进行动规。嗯,没问题。

 

但是,第一数据范围可能会卡掉几个点,第二,代码量会非常大,而且状态转移很难写。

 

那么我们用图论的方法吧!

 

嗯,没问题。

 

但是请想想图论的算法解决这道题又有点杀鸡用牛刀了。而且码量绝对不小。

 

那么,我们可以用多叉树转二叉树的思想来解决这个问题。

 

怎么转呢?

 

先想一个问题,在一棵多叉树中,兄弟的兄弟还是兄弟,儿子的兄弟也还是自己的儿子。兄弟的儿子就与自己没什么关联。

 

那么我们通过这个性质,构造一棵二叉树,令其满足任意一棵子树的根节点的非空左儿子是自己原来的儿子,非空右儿子是自己原来的兄弟。那么一棵树就建好了。

 

然后,想状态转移方程。自己的兄弟是和自己有同等的发展机遇的,所以根节点能经历的事,能分到的课程数,它现在的右儿子(兄弟)也同样能经历。所以也要先遍历。

至于儿子,可以儿子和兄弟一起分课程,那么一起去两个儿子也是可行的。

那么我们得到以下方程:f[root][len]=max(f[root][len],max(f[b[root]][len],f[b[root]][i]+f[c[root]][len-i-1]+w[root]));

设f[root][len]表示以root为根节点的时候取len门课程可以取得的最大收益。那么状态转移的状态就有完全不变、不选根节点(只选兄弟)和孩子兄弟一起分这三种情况,很好理解。

 

对于存储,可以选择链式前向星,用存图的方式来存储,那么dfs的时候就有些奇怪。

也可以用类似链表的形式存上一条边,同样快捷。

 

上代码:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cctype>
 5 using namespace std;
 6
 7 int w[310],b[310],c[310],f[310][310],n,m,t;            //w是权值,b是兄弟,c是孩子
 8
 9 inline int read()                                //读入优化
10 {
11     int f=1,x=0;
12     char c=getchar();
13     while (!isdigit(c))
14         f=c==‘-‘?-1:1,c=getchar();
15     while (isdigit(c))
16         x=(x<<1)+(x<<3)+(c^48),c=getchar();
17     return x*f;
18 }
19
20 void dfs(int root,int len)                            //记忆化
21 {
22     if (f[root][len]>=0)                            //出现过,直接返回
23         return;
24     if (root==0||len==0)                        //到边界了
25     {
26         f[root][len]=0;
27         return;
28     }
29     int i;
30     dfs(b[root],len);                            //兄弟和自己有同等的发展机遇
31     for (i=0;i<len;i++)                            //兄弟孩子一起分
32     {
33         dfs(b[root],i);
34         dfs(c[root],len-i-1);
35     f[root][len]=max(f[root][len],max(f[b[root]][len],f[b[root]][i]+f[c[root]][len-i-1]+w[root]));
36     }
37     return;
38 }
39
40 int main()
41 {
42     int i,j;
43     n=read();
44     m=read();
45     memset(f,-1,sizeof(f));                        //作死,初始化
46     for (i=1;i<=n;i++)
47     {
48         t=read();
49         w[i]=read();
50         if (!t)
51             t=n+1;
52         b[i]=c[t];                                //模拟链表
53         c[t]=i;
54     }
55     dfs(c[n+1],m);
56     printf("%d",f[c[n+1]][m]);
57     return 0;
58 }

总结一点,树规的dfs很好理解,从哪开始,从哪结束,从哪输出。

 

嗯,就这样了。

原文地址:https://www.cnblogs.com/Ronald-MOK1426/p/8448397.html

时间: 2024-10-25 08:36:07

【洛谷P2014】选课的相关文章

树形DP 洛谷P2014 选课

P2014 选课 题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b).一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少? 输入输出格式 输入格式: 第一行有两个整数N,M用空格隔开.(1<=N<=300,1<=M<=300) 接下来的N行

洛谷P2014——选课

题目:https://www.luogu.org/problemnew/show/P2014 树状DP,注意枚举当前子树中选几个时的边界. 代码如下: #include<iostream> #include<cstdio> using namespace std; int n,m,k,s[305],ct,cnt[305],dp[305][305],size[305]; struct N{ int to,next; }edge[305]; void add(int x,int y)

洛谷 P2014 选课

题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b).一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少? 输入输出格式 输入格式: 第一行有两个整数N,M用空格隔开.(1<=N<=300,1<=M<=300) 接下来的N行,第I+1行包含两

洛谷P2014 选课 (树形dp)

10月1日更新.题目:在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b).一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少?输入第一行有两个整数N,M用空格隔开.(1<=N<=200,1<=M<=150)接下来的N行,第I+1行包含两个整数ki和s

洛谷—— P2014 选课

https://www.luogu.org/problem/show?pid=2014 题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b).一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少? 输入输出格式 输入格式: 第一行有两个整数N,M用空格隔开.

洛谷P2014

P2014 选课 题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b).一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少? 输入输出格式 输入格式: 第一行有两个整数N,M用空格隔开.(1<=N<=300,1<=M<=300) 接下来的N行

洛谷P2014 TYVJ1051 选课

题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一门或没有直接先修课(若课程a是课程b的先修课即只有学完了课程a,才能学习课程b).一个学生要从这些课程里选择M门课程学习,问他能获得的最大学分是多少? 输入输出格式 输入格式: 第一行有两个整数N,M用空格隔开.(1<=N<=200,1<=M<=150) 接下来的N行,第I+1行包含两

洛谷 P2709 BZOJ 3781 小B的询问

题目描述 小B有一个序列,包含N个1~K之间的整数.他一共有M个询问,每个询问给定一个区间[L..R],求Sigma(c(i)^2)的值,其中i的值从1到K,其中c(i)表示数字i在[L..R]中的重复次数.小B请你帮助他回答询问. 输入输出格式 输入格式: 第一行,三个整数N.M.K. 第二行,N个整数,表示小B的序列. 接下来的M行,每行两个整数L.R. 输出格式: M行,每行一个整数,其中第i行的整数表示第i个询问的答案. 输入输出样例 输入样例#1: 6 4 3 1 3 2 1 1 3

洛谷1231 教辅的组成

洛谷1231 教辅的组成 https://www.luogu.org/problem/show?pid=1231 题目背景 滚粗了的HansBug在收拾旧语文书,然而他发现了什么奇妙的东西. 题目描述 蒟蒻HansBug在一本语文书里面发现了一本答案,然而他却明明记得这书应该还包含一份练习题.然而出现在他眼前的书多得数不胜数,其中有书,有答案,有练习册.已知一个完整的书册均应该包含且仅包含一本书.一本练习册和一份答案,然而现在全都乱做了一团.许多书上面的字迹都已经模糊了,然而HansBug还是可