洛谷

洛谷题单指南-树与图上的动态规划-P2014 [CTSC1997] 选课

原题链接:https://www.luogu.com.cn/problem/P2014

题意解读:课程之间有的有依赖,有的可以直接选,问选不超过m个课程的最大学分和。

解题思路:

对于没有依赖的课程,假定一个课程0,设依赖课程0,课程0必选,且学分为0。

这样一来,要选择不超过m个课程则变成选m + 1个课程。

接下来,就采用有依赖的背包问题求解。

1、状态表示

设f[i][j]表示在以i为根的子树中选不超过j门课的最大学分和

2、状态转移

将每一个子树看做分组

  从大到小枚举背包体积,即选择的节点数

    在每一个分组中枚举所有的物品,每一个物品就是在分组中选择所有少于背包体积的节点数

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

3、初始化

根节点u是必选的,f[u][1~m] = w[u]

4、结果

f[0][m]

100分代码:

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

const int N = 305;
vector<int> g[N];
int w[N];
int f[N][N]; //f[i][j]表示在以i为根的子树中选不超过j门课的最大学分和
int n, m;

void dfs(int u)
{
    for(int i = 1; i <= m; i++) f[u][i] = w[u]; //初始化,u必选
    for(int v : g[u])
    {
        dfs(v);

        for(int i = m; i >= 0; i--) 
            for(int j = 0; j < i; j++) //子树分组选择的数量比体积小1,给根节点留一个位置
                f[u][i] = max(f[u][i], f[v][j] + f[u][i - j]);
    }
}

int main()
{
    cin >> n >> m;
    m++; //根节点0必选,容量增加一个
    for(int i = 1; i <= n; i++)
    {
        int k, s;
        cin >> k >> s;
        g[k].push_back(i);
        w[i] = s;
    }
    dfs(0);
    cout << f[0][m];
    return 0;
}