题解:洛谷 P2014 [CTSC1997] 选课

【题目来源】

洛谷:P2014 [CTSC1997] 选课 - 洛谷

【题目描述】

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

【输入】

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

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

【输出】

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

【输入样例】

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

【输出样例】

13

【算法标签】

《洛谷 P2014 选课》 #动态规划,dp# #搜索# #树形dp# #WC|CTSC|集训队# #1997#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

int n, m;               // n: 课程总数(不包括虚拟根节点0),m: 需要选择的课程数
int s[305];             // 存储每门课程的学分
vector<int> G[305];     // 图的邻接表表示,G[x]表示x的所有子节点(课程)
int f[305][305][305];   // 动态规划数组,f[x][i][j]表示以x为根的子树中,考虑前i个子树,选j门课的最大学分

/**
 * 深度优先搜索进行动态规划
 * @param x 当前处理的课程节点
 */
void dfs(int x)
{
    // 先递归处理所有子节点
    for (auto v : G[x])
    {
        dfs(v);
    }

    // 初始化:选择当前节点或不选当前节点
    f[x][1][1] = s[x];  // 选择当前节点
    f[x][1][0] = 0;     // 不选当前节点

    // 动态规划处理
    for (int i = 2; i < G[x].size() + 2; i++)  // 遍历所有子节点(+2是因为初始i从2开始)
    {
        int v = G[x][i - 2];  // 当前处理的子节点
        for (int j = 0; j <= n; j++)          // 遍历可能选择的课程数
        {
            for (int k = 0; k < j; k++)       // 遍历在当前子树中可能选择的课程数
            {
                // 状态转移方程
                f[x][i][j] = max(f[x][i][j], 
                                f[v][G[v].size() + 1][k] + f[x][i - 1][j - k]);
            }
        }
    }
}

int main()
{
    // 输入数据
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        int x;
        cin >> x >> s[i];          // 输入先修课和学分
        G[x].push_back(i);         // 构建课程依赖关系图
    }

    // 从虚拟根节点0开始处理
    dfs(0);

    // 输出结果:在虚拟根节点下选择m+1门课(包含虚拟根节点)
    cout << f[0][G[0].size() + 1][m + 1] << endl;

    return 0;
}

【运行结果】

7  4
2  2
0  1
0  4
2  1
7  1
7  6
2  2
13
posted @ 2026-02-20 19:42  团爸讲算法  阅读(3)  评论(0)    收藏  举报