zoukankan      html  css  js  c++  java
  • 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门课程的最大得分。

    解决问题

    "先修课"的关系就如同树上的父子节点一样.因此通过以"先修课关系"为依据连好边以后,这个问题的模型将呈现树形.这是一道树形DP的经典题目.我们设f[i][j]表示在以i为根的这棵树内选取k门课,所能得到的最大学分和.为什么能这样呢?因为树形非常符合DFS的模型,能够做到不重不漏,所以树形DP通常用递归的方式实现.那么我们显然需要考虑到设置状态的时候应该与什么有关.DFS时,我们总是一次次到达子树的根节点,然后再继续考虑,所以我们应当将其中一维与子树的根相关.然后我们想要得到最大学分,这要通过适当的选择科目来实现.因此我们枚举选择j门课,能得到的最大学分.最终目标f[0][m].

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cctype>
    #include <utility>
    #include <vector>
    using namespace std;//树形DP经典例题_选课 
    
    inline void read(int &x) {
    	x = 0;
    	char ch = getchar(), w = 0;
    	while (!isdigit(ch)) w = (ch == '-'), ch = getchar();
    	while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    	x = w ? -x : x;
    	return;
    }
    
    int n, m, sc[305], f[305][305];//f[i][j]表示以i为根的子树中选j门课能得到的最多学分 
    vector<int> son[305];
    
    void tree_dp(int now) {//树形DP惯用记忆化搜索 
    	f[now][0] = 0;//初值为0; 
    	for (int i = 0; i < son[now].size(); ++i) {//扫描所有儿子 
    		int v = son[now][i];
    		tree_dp(v);//递归到底再从叶子返回根 
    		for (int t = m; t >= 0; --t)//倒序枚举选几门课,因为每一节课只能选一次,代表这属于01背包,如果正序就是完全背包了. 
    			for (int j = t; j >= 0; --j)
    				if (t - j >= 0)
    					f[now][t] = max(f[now][t], f[now][t - j] + f[v][j]);//相当于从目前枚举出的总课数中,
    																	//有j门是在儿子i选的,其他的是在其他儿子选的. 
    	}
    	if (now != 0)
    		for (int t = m; t > 0; --t)
    			f[now][t] = f[now][t - 1] + sc[now];//因为树根也要选,所以所有情况都要加上树根 
    	return;
    }
    
    int main() {
    	read(n), read(m);
    //	memset(f, 128, sizeof(f));
    	for (int i = 1, a, b; i <= n; ++i) {//建树,读入score 
    		read(a), read(b);
    		son[a].push_back(i), sc[i] = b;
    	}
    	tree_dp(0);//从树根开始dp ,这个树根是超级原点 
    	cout << f[0][m] << endl;//最终目标,在以0为根的子树中选m门课的最高学分 
    	return 0;
    }
    

    欢迎批评和指正

  • 相关阅读:
    SQL 数据库 复制 与订阅 实现数据同步
    SQL 2008配置管理工具服务显示 远程过程调用失败0x800706be
    SQL2005中使用identity_insert向自动增量字段中写入内
    【树莓派】【转载】基于树莓派,制作家庭媒体中心+下载机
    Linux 按时间批量删除文件(删除N天前文件)
    【树莓派】为树莓派配置或扩展swap分区
    开源硬件相关平台
    【树莓派】树莓派上刷android系统
    【树莓派】树莓派上面安装配置teamviewer
    【树莓派】使用xdrp远程登录树莓派的图形界面
  • 原文地址:https://www.cnblogs.com/i-cookie/p/11383837.html
Copyright © 2011-2022 走看看