洛谷 P2014 选课

Description

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

Input

  • 第一行有两个整数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)。

Output

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

Sample Input

Sample Output

题解:

  • 树形dp。
  • 首先他可能是个森林,所以给它来一个虚点0。观察发现,这是一个有依赖的树形dp。那么我们就按照背包的思路在树上进行dp。dp(i, j)表示在以i为根节点的子树中选j门课的最大价值。转移比较容易,第一层(i)枚举子树节点,第二层(j)枚举当前节点x的子节点,第三层(k)从m - 1枚举到1(即枚举背包容量,m - 1是因为x这个点必须要选),第四层(l)从0枚举到k(即给当前儿子用多少容量)。
#include <iostream>
#include <cstdio>
#define N 305
using namespace std;

struct E {int next, to;} e[N];
int n, m, num;
int h[N], in[N], w[N];
int dp[N][N];
bool vis[N];

void add(int u, int v)
{
    e[++num].next = h[u];
    e[num].to = v;
    h[u] = num;
}

void dfs(int x)
{
    vis[x] = 1;
    for(int i = h[x]; i != 0; i = e[i].next)
        if(!vis[e[i].to]) dfs(e[i].to);
    for(int i = h[x]; i != 0; i = e[i].next)
    {
        int now = e[i].to;
        for(int j = m - w[x]; j >= w[now]; j--)
            for(int k = 0; k <= j; k++)
                dp[x][j + w[x]] = max(dp[x][j + w[x]], dp[x][j + w[x] - k] + dp[now][k]);
    }
}

int main()
{
    cin >> n >> m;
    for(int i = 1; i <= n; i++)
    {
        int to;
        cin >> to >> dp[i][1];
        w[i] = 1, add(to, i);
    }
    dfs(0);
    cout << dp[0][m];
    return 0;
}
02-01 03:41