首页 > 其他分享 >选课 题解

选课 题解

时间:2024-02-16 21:44:37浏览次数:23  
标签:修课 选课 int 题解 课程 学分 选修 DP

题目描述

大学里实行学分。每门课程都有一定的学分,学生只要选修了这门课并考核通过就能获得相应的学分。学生最后的学分是他选修的各门课的学分的总和。

每个学生都要选择规定数量的课程。其中有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。 例如,《数据结构》必须在选修了《高级语言程序设计》之后才能选修。我们称《高级语言程序设计》是《数据结构》的先修课。 每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。为便于表述每门课都有一个课号,课号依次为1,2,3,……。 下面举例说明:

image

上例中1是2的先修课,即如果要选修2,则1必定已被选过。同样,如果要选修3,那么1和2都一定已被选修过。

学生不可能学完大学所开设的所有课程,因此必须在入学时选定自己要学的课程。每个学生可选课程的总数是给定的。现在请你找出一种选课方案,使得你能得到学分最多,并且必须满足先修课优先的原则。假定课程之间不存在时间上的冲突。

输入格式

输入文件的第一行包括两个正整数M、N(中间用一个空格隔开)其中M表示待选课程总数(1≤M≤1000),N表示学生可以选的课程总数(1≤N≤M)。

以下M行每行代表一门课,课号依次为1,2……M。每行有两个数(用一个空格隔开),第一个数为这门课的先修课的课号(若不存在先修课则该项为0),第二个数为这门课的学分。学分是不超过10的正整数。

输出格式

输出文件第一行只有一个数,即实际所选课程的学分总数。

样例

样例输入

7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2

样例输出

13

题解

不难看出,这道题的数据结构是树,对于某些课程,选它就必选其它某一门课程,并且根节点的最优解可以由子树转移而来,再看看数据范围,这不就是有依赖的背包DP--树形背包吗;

我们采用性能比较中庸的链式前向星存图,让每一个被依赖的指向依赖的(即父亲指向儿子),发现会存出一个森林,为了我们使用树形DP,可以模拟一个虚点0来连接每个根,这样就得到了一棵树;

下面就开始DP,由于树本身的DFS性,我们用DFS的形式来DP;

首先考虑状态设计,定义f[i][j][k]表示以i为根的前j棵子树,选了k门课程的最大学分;

按背包DP的思想考虑,f[i][j][k] 应由f[i][j - 1][k - u] + f[v][vv][u] 转移而来;其中,u为选的课程数,需要枚举,vv代表v的子树总个数;

所以---

状态转移方程

f[i][j][k] = max(f[i][j][k], f[i][j - 1][k - u] + f[v][vv][u]);

剩下就是初始化了,很容易想到,不管选哪棵子树,f[x][1 ~ x的子树总个数][1] = 选x这门课的学分;

OK,你可以开始写了;

但作为追求卓越的先锋,三维仍然是有些冗余;

考虑优化;

想到01背包二维转化成一维的优化(滚动数组),我们也可以借鉴一下,将第二维优化掉,改成倒序循环;

定义f[i][j]表示以i为根,选了j门课程的最大学分;

于是---

新的状态转移方程

f[i][j] = max(f[i][j], f[i][k] + f[v][j - k]);
其中,v为i的子树,k需要枚举;

初始化:f[x][1] = 选x这门课的学分;

代码

#include <iostream>
#include <cstring>
using namespace std;
int n, m;
int f[1005][1005];
int a[1005];
struct sss{
	int t, ne;
}e[10005];
int h[10005], cnt;
void add(int u, int v) {
	e[++cnt].t = v;
	e[cnt].ne = h[u];
	h[u] = cnt;
}
int dfs(int x) {
	int p = 1; //子树的课程总个数,最小是叶子节点1;
	f[x][1] = a[x]; //初始化;
	for (int i = h[x]; i != -1; i = e[i].ne) {
		int s = dfs(e[i].t);
		for (int j = min(p, m + 1); j; j--) { //j循环根节点课程个数,对于本阶段是p(总个数);
			for (int k = 1; k + j <= m + 1 && k <= s; k++) { //k循环子树课程个数,上限是s,同时要满足k + j <= m + 1;
				f[x][j + k] = max(f[x][j + k], f[x][j] + f[e[i].t][k]);
			}
		}
		p += s; //将每次DFS的子树的课程个数累加;
	}
	return p; //返回课程个数;
}
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		e[i].ne = 0;
		e[i].t = 0;
	}
	memset(h, -1, sizeof(h));
	memset(f, 0, sizeof(f));
	memset(a, 0, sizeof(a));
	cnt = 0;
	int b;
	for (int i = 1; i <= n; i++) {
		cin >> b >> a[i];
		add(b, i);
	}
	dfs(0);
	cout << f[0][m + 1] << endl; //最多选m + 1门课,因为还有虚点
	cin >> n >> m;
	return 0;
}

标签:修课,选课,int,题解,课程,学分,选修,DP
From: https://www.cnblogs.com/PeppaEvenPig/p/18017514

相关文章

  • 洛谷 P4065 题解
    模拟赛T1,纪念一下第一次场切紫。(话说场上这么多人切是不是都找到原了,就我这么傻想了半天)正难则反,很容易的将题目转化为选择若干种颜色,使这些颜色在原数组中的位置连续。设$pre_i$为颜色$i$最早出现的位置,$suf_i$为颜色$i$最晚出现的位置。假设通过选择若干颜色得到的位......
  • P10149 [Ynoi1999] XM66F题解
    题解首先,问题是静态的区间查询问题,一眼莫队。那么我们就需要考虑所需要维护的内容在区间扩增或者缩减时的变化如何快速维护。我们可以先写出对于区间\([l,r]\)来说,满足\(l\lei<j<k\ler\)的有序三元组\((i,j,k)\)数量的表达式,方便拆式子:\[\sum\limits_{i=l}^{r}......
  • 启动vue-element-admin遇到问题解决方案
    概述从https://github.com/PanJiaChen/vue-element-admin下载代码,按照文档执行,期间遇到一些列问题。1#clonetheproject2gitclonehttps://github.com/PanJiaChen/vue-element-admin.git34#entertheprojectdirectory5cdvue-element-admin67#insta......
  • 出现8080端口占用问题解决
    查到占用端口号并关闭netstat-aon|findstr8080出现:TCP0.0.0.0:80000.0.0.0:0LISTENING23296TCP[::]:8000[::]:0LISTENING23296tasklist|findstr"23296"出现:java.exe......
  • CF739A Alyona and mex 题解
    题目简单构造,首先我们知道一个区间\([l,r]\)内的最大答案为为这个区间的长度\(len\),因为其中可以包括\([0,r-l+1]\)这些数。所以\(ans=min(len_i)\)。考虑如何满足这个条件,设最小长度为\(len_{min}\),我们可以轮流输出\([0,len_{min}]\),因为\(len_{min}\)是最小长度,所......
  • 树状数组-三色二叉树 题解
    题目在这里————————————————————————————————三色二叉树首先题面写的很清楚了是一道树状数组题因为这题的输入方式很特别按二叉树序列所以在输入上要特殊处理如下voidread(intx){//读入+存图以左右子树为形式如l[x]=y即y为x左子树......
  • [Vijos P1448] 校门外的树 题解
    题目描述校门外有很多树,学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两种操作:k==1,读入l,r表示在l到r之间种上一种树,每次操作种的树的种类都不同;k==2,读入l,r表示询问l到r之间有多少种树。注意:每个位置都可以重复种树。......
  • CF55D Beautiful numbers 题解
    题目链接:CF或者洛谷常见知识点组合经典题。首先,一眼数位dp类型题,考虑需要处理些怎样的判断合法数位信息。经典操作对于跟整除有关的判断,数位dp为了减少使用空间,都可以考虑记忆化模数减少空间开销。对于整除若干个数,即整除这若干个数的最小公倍数即可,是一个非常常用......
  • 【题解】P4722 【模板】最大流 加强版 / 预流推进
    更好阅读体验CHAPTER0废话1.常见的最大流算法可以大致分为两种:找增广路和预流推进2.从理论时间复杂度上界来看,找增广路算法的最坏时间复杂度为\(O(n^2m)\),而预流推进算法的最坏时间复杂度为\(O(n^2\sqrt{m})\),看起来预流推进要显然优于找增广路,但在实际应用(尤指OI)中,由于包......
  • HDU-Employment Planning题解
    题目在这里————————————————————————————————EmploymentPlanning简单的一道dp关键的点在于想到用枚举实现各种情况的讨论关键的注释写在代码里了还是很清晰的捏~#include<bits/stdc++.h>#definefo(x,y,z)for(int(x)=(y);(x)<=(z);(x......