『选课 树形dp 输出方案』
<更新提示>
<第一次更新>这道题的树上分组背包的做法已经在『选课 有树形依赖的背包问题』中讲过了,本篇博客中主要讲解将多叉树转二叉树的做法,以便输出方案。
<正文>
选课
Description
学校实行学分制。每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。学校开设了N(N < 500)门的选修课程,每个学生可选课程的数量M是给定的。学生选修了这M门课并考核通过就能获得相应的学分。
在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。例如《Frontpage》必须在选修了《Windows操作基础》之后才能选修。我们称《Windows操作基础》是《Frontpage》的先修课。每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。每门课都有一个课号,依次为1,2,3,…。
上例中1是2的先修课,即如果要选修2,则1必定已被选过。同样,如果要选修3,那么1和2都一定已被选修过。 你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修课优先的原则。假定课程之间不存在时间上的冲突。
Input Format
第一行包括两个正整数N、M(中间用一个空格隔开)其中N表示待选课程总数(1≤N≤500),M表示学生可以选的课程总数(1≤M≤N)。 以下M行每行代表一门课,课号依次为1,2,…,M。每行有两个数(用一个空格隔开),第一个数为这门课的先修课的课号(若不存在先修课则该项为0),第二个数为这门课的学分。
Output Format
第一行只有一个数,即实际所选课程的学分总数。 以下N行每行有一个数,表示学生所选课程的课号。
n行学生选课的课号按从小到大的顺序输出。
Sample Input
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
Sample Output
13
2
3
6
7
解析
直接使用树上背包的方法,不便于输出方案,我们换一种思路\(dp\)。对于每一个节点,我们记录\(br[i]\)代表\(i\)一个兄弟节点的编号,\(ch[i]\)代表\(i\)一个子节点的编号。如果兄弟节点和子节点不止一个怎么办,显然,它兄弟的兄弟也是它的兄弟,它儿子的兄弟也是它的儿子,这样就可以表示所有的兄弟节点和儿子节点了,而事实上,对于每一个\(i\)我们只记录一个\(br[i]\)和\(ch[i]\),这样,本质上我们就把多叉树转换为二叉树了。
还是设\(f[x][t]\)代表以\(x\)为根的子树中选了\(t\)门课的最大学分,显然有两种转移:
\(1.\) 不取节点\(x\),直接令\(f[x][t]=f[br[x]][t]\)即可
\(2.\) 取节点\(x\)并在以\(x\)为根的子树中取一部分点,剩下的一部分点在兄弟中取,即\(f[x][t]=max\{f[br[x]][i]+f[ch[x]][t-i-1]+a[x]\}\)
利用上述两个方程即可完成树形\(dp\)。
我们可以通过同样的枚举方式得知每一次节点\(x\)是否被选,就能得到方案了。
\(Code:\)
#include<bits/stdc++.h>
using namespace std;
const int N = 520;
int n,m,br[N],ch[N],a[N],f[N][N],ans[N];
inline void input(void)
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
int fa; scanf("%d%d",&fa,&a[i]);
if ( fa == 0 ) fa = n+1;
br[i] = ch[fa];
ch[fa] = i;
}
}
inline void dp(int x,int t)
{
if ( f[x][t] > 0 ) return;
if ( x == 0 || t == 0 ) return;
dp( br[x] , t );
f[x][t] = f[br[x]][t];
for (int i=0;i<t;i++)
{
dp( br[x] , i ) , dp( ch[x] , t-i-1 );
f[x][t] = max( f[x][t] , f[br[x]][i] + f[ch[x]][t-i-1] + a[x] );
}
}
inline void solve(int x,int t)
{
if ( x == 0 || t == 0 ) return;
if ( f[x][t] == f[br[x]][t] ) return solve( br[x] , t );
for (int i=0;i<t;i++)
{
if ( f[x][t] == f[br[x]][i] + f[ch[x]][t-i-1] + a[x] )
{
solve( br[x] , i ) , solve( ch[x] , t-i-1 );
ans[x] = true; break;
}
}
}
int main(void)
{
input();
dp( ch[n+1] , m );
solve( ch[n+1] , m );
printf("%d\n",f[ch[n+1]][m]);
for (int i=1;i<=n;i++)
if ( ans[i] )
printf("%d\n",i);
return 0;
}
<后记>
『选课 树形dp 输出方案』的更多相关文章
- 『大 树形dp』
大 Description 滑稽树上滑稽果,滑稽树下你和我,滑稽树前做游戏,滑稽多又多.树上有 n 个节点,它们构成了一棵树,每个节点都有一个滑稽值. 一个大的连通块是指其中最大滑稽值和最小滑稽值之差 ...
- 『kamp 树形dp』
kamp Description jz 市的云台山是个很美丽的景区,小 x 暑期到云台山打工,他的任务是开景区的大巴. 云台山景区有 N 个景点,这 N 个景点由 N-1 条道路连接而成,我们保证这 ...
- 选课 树形DP+多叉树转二叉树+dfs求解答案
问题 A: 选课 时间限制: 1 Sec 内存限制: 128 MB 题目描述 大 学里实行学分.每门课程都有一定的学分,学生只要选修了这门课并考核通过就能获得相应的学分.学生最后的学分是他选修的各门 ...
- vijos 1180 选课 树形DP
描述 学校实行学分制.每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分.学校开设了N(N<300)门的选修课程,每个学生可选课程的数量M是给定的.学生选修了这M门课并考核通过就能获得 ...
- 洛谷P2014 选课 (树形dp)
10月1日更新.题目:在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分 ...
- [vijos1880]选课<树形dp>
题目链接:https://www.vijos.org/p/1180 这是一道树形dp的裸题,唯一的有意思的地方就是用到了多叉树转二叉树 然后本蒟蒻写这一道水题就是因为以前知道这个知识点但是没有怎么去实 ...
- [Luogu P2014]选课 (树形DP)
题面 传送门:https://www.luogu.org/problemnew/show/P2014 Solution 这是一道十分经典的树形DP题,这种类型的树形DP有一种很普遍的解法. 首先,观察 ...
- Codevs1378选课[树形DP|两种做法(多叉转二叉|树形DP+分组背包)---(▼皿▼#)----^___^]
题目描述 Description 学校实行学分制.每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分.学校开设了N(N<300)门的选修课程,每个学生可选课程的数量M是给定的.学生选修 ...
- codevs 1378选课 树形DP
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; ],tr[] ...
随机推荐
- HTTP是什么,不是什么?
HTTP是什么? 不是什么? HTTP: 超文本传输协议,也就是HyperText Transfer Protocol.这是从字面上的理解,如果深一步的理解呢? HTTP又是什么呢? HTTP是什么 ...
- K8S 上搭建 Redis
根据需求搭建一个不需要数据持久化,需要密码登录的 Redis mkdir /iba/qa_ibaboss_elk -p cd /iba/qa_ibaboss_elk # 创建一个专用的 namespa ...
- Linux shell awk中printf使用
printf 是 awk 的重要格式化输出命令 printf格式化输出内容 格式: printf format,item1,item2... 要点: 1,printf输出时要指定格式f ...
- flask实战-个人博客-编写博客前台
编写博客前台 博客前台需要开放给所有用户,这里包括显示文章列表.博客信息.文章内容和评论等功能功能. 分页显示文章列表 为了在主页显示文章列表,我们要先在渲染主页模板的index视图的数据库中获取所有 ...
- UiPath: Selectors repair 选择器的修复,即被选择的按钮发生改变如何选择第二按钮
实现批量注册用户功能时,出现第一个用户注册完时,弹出确认按钮,点击即可,但是第二个用户注册完成时,弹出的按钮与第一个有差异,图形用户界面元素及其父元素的属性都发生改变.所以就点不了按钮,就卡死在这.如 ...
- 17-C#笔记-类
1. 构造函数.析构函数 修饰词的作用域仅一行. 类的默认访问标识符是 internal,成员的默认访问标识符是 private. using System; namespace LineApplic ...
- c语言中的数据变量类型,大小
C中有哪些数据类型? 回答: 有两种类型的数据类型,用户定义和预定义.预定义的数据类型是int,char,float,double等,用户使用标签struct,union或enum创建用户定义的数据类 ...
- mybatis框架-resultMap的自动映射级别-partial 和full的探讨
现在我们做一个小实验,输出一下上一个案例中没有匹配的属性,注意哦,现在user类中是有内部嵌套的复杂数据类型的 运行结果: 注意到:现在居然连userPassword都打印不出来了,原因就是user类 ...
- Spring Boot版本号说明
Spring Boot的版本选择一般是这样的,如下图: 那版本号后面的英文代表什么含义呢? 具体含义,如下文所示: SNAPSHOT:快照版,表示开发版本,随时可能修改: M1(Mn):M是miles ...
- hasura graphql-engine + plv8 集成
hasura graphql-engine 是一款很不错的基于pg 的graphql 引擎,plv8 是pg 的一个扩展,我们可以使用js 编写 函数 ,触发器,而且对于es6 的语法也有比较完备的支 ...