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

我们用到的是孩子兄弟法。
对于每一个节点,他在二叉树中的左孩子是在多叉树中的第一个孩子,他在二叉树中的右孩子是在多叉树中的第一个兄弟。
如上图,左边的是原来的多叉树,右边的是二叉树,其中红色线表示左孩子的关系,绿色线表示右孩子的关系。
比如节点2,在多叉树中的第一个孩子是6,那么他在二叉树中的左孩子是6。第一个兄弟是3,所以在二叉树中的右孩子是3。
为什么我们要转换呢?因为方便记忆化搜索处理状态啊!
因为输入的是一个森林,我们就通过节点0来代表一棵树的根的父亲(这是很方便的,因为输入数据就是这样的:若ki=0表示没有直接先修课)。
------------------------------------------------------------------
2.然后是树形dp。我这里用的记忆化搜索。
dfs(i,j)代表当前要考虑第i个课程及以其为根的二叉树中的子树的选择情况,j表示当前能够选择多少门课程。
根据题意可知,现在正在考虑第i门课程的情况时,必须已经选择了第i门课程在多叉树中的爹(要不选个毛啊)。所以我们可以直接略过第i门课程,去考虑第i门课程的右孩子(多叉树中的下一个兄弟),当然也不能考虑自己的左孩子(多叉树中的孩子)了。
我们还需要选第i门课程的情况。选第i门课程,就得加上这门课程的学分,然后剩余总课程--。剩下的课程可以分给自己的左孩子(多叉树中的孩子)一些,剩下的分给自己的右孩子(多叉树中的兄弟)。在dfs中,for(int k=0;k<j;k++)这里面的这个k就是给左孩子多少。那么就更新答案为。自己的学分+dfs左孩子的学分+dfs右孩子的学分。
简洁的main函数就是读入->转换->dfs并输出答案。这里我们不dfs 0,而是直接dfs 0的左孩子(之前说过0是整个森林的根),是因为防止在边界判断时候把0判断掉。
 3.代码
#include <iostream>
using namespace std;
int n,m;
int a[2001],f[2001];//a[i]是某一门课程的学分 f[i]是某一门课程在多叉树中的爹
int lChild[2001],rChild[2001];//二叉树的左子树和柚子树
int dp[2001][2001];//dp[i][j]是以第i个节点为根的二叉树(注意是二叉树),能选择j门课程时候的最大学分。
void convert()//多叉树 -> 二叉树
{
        for(int i=1;i<=n;i++)//遍历每一个节点
        {
                int fa=f[i];//这个节点的爹
                if(lChild[fa]==0)lChild[fa]=i;//这个节点的爹还没有孩子,所以这个节点就是这个节点的爹的第一个孩子
                else//这个节点的爹有孩子,所以这个节点在二叉树中就应该是这个节点的爹的左孩子的最右边的叶子的孩子
                {
                        fa=lChild[fa];//暂时让变量fa变成爹的第一个孩子(二叉树中的左孩子)
                        while(rChild[fa])fa=rChild[fa];//循环找最右边的叶子
                            rChild[fa]=i;//成为最右边的叶子的右孩子
                }
        }
}
int dfs(int i,int j)//i是当前节点的编号,j是当前可以选择的课程数目
{
        if(i<1||j<1||i>n||j>m)return 0;//边界判断
        if(dp[i][j]!=0)return dp[i][j];//查备忘录
        for(int k=0;k<j;k++)//选给左子树k个,自己留一个(得选自己,否则无法选左孩子),剩下的给右
                dp[i][j]=max(dp[i][j],a[i]+dfs(lChild[i],k)+dfs(rChild[i],j-k-1));//自己的学分+dfs左孩子的学分+dfs右孩子的学分
        dp[i][j]=max(dp[i][j],dfs(rChild[i],j));//略过自己,都给右孩子
        return dp[i][j];//最后返回答案
}
 
int main()
{
        cin >> n >> m;
        for(int i=1;i<=n;i++)
                cin >> f[i] >> a[i];
        convert();
        cout << dfs(lChild[0],m) << endl;
        return 0;
}

洛谷P2014 选课 (树形dp)的更多相关文章

  1. 树形DP 洛谷P2014 选课

    洛谷P2014 选课 题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门 ...

  2. 洛谷 P2014 选课(树形背包)

    洛谷 P2014 选课(树形背包) 思路 题面:洛谷 P2014 如题这种有依赖性的任务可以用一棵树表示,因为一个儿子要访问到就必须先访问到父亲.然后,本来本题所有树是森林(没有共同祖先),但是题中的 ...

  3. [Luogu P2014]选课 (树形DP)

    题面 传送门:https://www.luogu.org/problemnew/show/P2014 Solution 这是一道十分经典的树形DP题,这种类型的树形DP有一种很普遍的解法. 首先,观察 ...

  4. 洛谷P2014 选课

    首先分析题目,这是一道树形dp的题目,是树形背包类的问题,以为每门课的先修课只有一门,所以这一定可以 构成一个森林结构,于是我们可以设计一个虚拟的根节点作为森林的根. 状态转移方程如下 dp[v][k ...

  5. 洛谷P2014——选课

    题目:https://www.luogu.org/problemnew/show/P2014 树状DP,注意枚举当前子树中选几个时的边界. 代码如下: #include<iostream> ...

  6. 洛谷 P2014 选课

    题目描述 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习,在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习.现在有N门功课,每门课有个学分,每门课有一 ...

  7. 洛谷 P2014 选课 && caioj 1108 树形动态规划(TreeDP)3:选课

    这里的先后关系可以看成节点和父亲的关系 在树里面,没有父亲肯定就没有节点 所以我们可以先修的看作父亲,后修的看作节点 所以这是一颗树 这题和上一道题比较相似 都是求树上最大点权和问题 但这道题是多叉树 ...

  8. 洛谷2015(树形dp)

    要点 是树形的考虑dfs 分为取一枝,取两枝两种情况,将它们的合法情况进行暴举取最好答案即可,貌似我乱搞得相当冗-- 顺手记忆化 正解应该是树上背包 #include <cstdio> # ...

  9. 洛谷 P1411 树 (树形dp)

    大意: 给定树, 求删除一些边, 使得连通块大小的乘积最大 设$dp_{i,j}$表示只考虑点$i$的子树, $i$所在连通块大小为$j$的最大值. 转移的时候不计算$i$所在连通块的贡献, 留到最后 ...

随机推荐

  1. OneNote 2013 快捷键

    越来越喜欢onenote这个笔记本软件,找了下提高效率的办法,收藏学习下: 转载自:http://onenoter.com/2013/04/5792 记录笔记和设置笔记格式 键入和编辑笔记 操作 按键 ...

  2. JavaScript中 Promise的学习以及使用

    今天一个哥们发过来一段js代码,没看懂,就顺便学习了一下,代码如下  Promise.resolve('zhangkai').then(value => {console.log(value)} ...

  3. Intellij Idea系列之Tomcat环境的搭建(三)

    Intellij Idea系列之Tomcat环境的搭建(三) 一. 编写背景 Intellij Idea在刚上手的时候很多人吐槽,"god, 这么难用的IDE有谁用呀?",的确,I ...

  4. Android OpenCV 图像识别

    最近打算写一个android 平台opencv 的小程序,着手查找了一下资料.网络上的资料参差不齐,有一些都比较老旧,我参考了前面的方法找到了一个简单的搭建方法,分享给大家. 0,环境的搭建: jav ...

  5. 在 Windows上配置NativeScript CLI

    1.安装Node.js,到https://nodejs.org/下载安装 2.安装Chocolatey,https://chocolatey.org/,先看一下关于chocolatey的介绍: 安装方 ...

  6. angularjs如何在视图渲染结束之后,或者render之后执行指令中的link方法呢?

    angularjs如何在视图渲染结束之后,或者render之后执行指令中的link方法 关键字: $timeout app.directive("myDirective",func ...

  7. 在sqlserver 中with(nolock)详解

      所有Select加 With (NoLock)解决阻塞死锁        在查询语句中使用 NOLOCK 和 READPAST  处理一个数据库死锁的异常时候,其中一个建议就是使用 NOLOCK  ...

  8. 数据结构&算法-单链表

    1.引言 工作一年了,感觉越来越懒散,把很多基础性的东西都慢慢遗忘了,最近想趁着还没忘完,回顾一下,整理了点笔记,分享一下. 如有错的地方,欢迎大家怒喷. 2.学习 我们就从最简单的链表开始吧. 链表 ...

  9. MSSQL导入导出数据

    /******* 导出到excel */ EXEC master..xp_cmdshell 'bcp SettleDB.dbo.shanghu out c:\temp1.xls -c -q -S&qu ...

  10. Windows窗口自动缩放机制

    通过自动缩放功能,能使在一个计算机上设计的界面在另一个具有不同分辨率或系统字体的计算机上能正常显示.这样窗体及其控件就能通过智能化调整大小以保障在本地电脑和用户电脑上保持一致. 自动缩放的必要性 如果 ...