1. 程式人生 > >CTSC1998 選課(揹包類樹形Dp)

CTSC1998 選課(揹包類樹形Dp)

題意:

給出 n 節課的先修課號以及學分(先修課號指的是在學習某節課時先需要學習的課程),求學 m 節課的最大學分。

細節:

1、對於課程 a 其先修課號為 b ,對於課程 b 其先修課號為 c ,則需要學 a 的方式必須為先學 c 在學 b
2、可能存在多門課程沒有先修課號。

分析:

題目給出的先修課號是唯一的,所以我們可以將這種依賴關係構建成一棵樹,所以對於每個節點的學分之和就是為從根節點到其的簡單路徑,所以對於每個節點相當於一個揹包。

所以狀態就是:dp[u][j] 表示以 u 為根的子樹選了 j 門課所獲得最大學分

轉移就是:dp[u][j] = max( dp[u][j] , dp[u][j-k] + dp[v][k] )

 
其中 dp[u][1] = dist[u] , 1 ≤ j ≤ size[u] , 0≤k≤min( size[v] , j-1)

程式碼:

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

int f[MAXN][MAXN], dist[MAXN], size[MAXN], n, m;
vector <int> G[MAXN];

void build(int u){
    size[u]=1;
    for (int i=0; i<G[u].size(); i++){
        int v=G[u][i];
        build(v);
        size[u]+=size[v];
    }

}

void solve(int u){
    f[u][1]=dist[u];
    for (int i=0; i<G[u].size(); i++){
        int v=G[u][i];
        solve(v);
        for (int j=min(size[u], m+1); j>=2; j--)
            for (int k=0; k<=min(size[v], j-1); k++)
                f[u][j]=max(f[u][j], f[u][j-k]+f[v][k]); 
    }
}

int main(){
    scanf("%d%d", &n, &m);
    for (int i=1, x; i<=n; i++){
        scanf("%d%d", &x, &dist[i]);
        G[x].push_back(i);
    }
    memset(f, 0, sizeof f);
    build(0);
    solve(0);
    printf("%d\n", f[0][m+1]);
    return 0;
}