1. 程式人生 > 其它 >洛谷P2014—選課(樹形DP)

洛谷P2014—選課(樹形DP)

題目

在大學裡每個學生,為了達到一定的學分,必須從很多課程裡選擇一些課程來學習,在課程裡有些課程必須在某些課程之前學習,如高等數學總是在其它課程之前學習。現在有 N 門功課,每門課有個學分,每門課有一門或沒有直接先修課(若課程 a 是課程 b 的先修課即只有學完了課程 a,才能學習課程 b)。一個學生要從這些課程裡選擇 M 門課程學習,問他能獲得的最大學分是多少?

輸入輸出

輸入:第一行輸入N,M。下面N行輸入兩個整數ki和si,分別為第i門課的先修課序號,第i門課的學分。
輸出:選M門課的最大學分

思路

這道題輸入的是森林,並不是一棵樹,需要加一個結點,把每個子樹連到根節點,構成一棵樹,即加入0結點作為虛擬課程,學分為0。
狀態轉移陣列


dp[i][j]為以i為根的子樹選j門課程獲得的最大學分。
0結點是必選的,所以需要m+1。
狀態轉移方程
dp[u][i](i>=1)初始化為s[u]為u的學分。
dp[u][j] = max(dp[u][j],dp[v][k] + dp[u][j-k]);

#include <iostream>
#include<algorithm>
#include<cmath>
using namespace std;

const int MAX_N = 310; //最多結點數
int tot;    //標記邊的序號
int head[MAX_N],nxt[MAX_N],ver[MAX_N];  //建樹要用到的陣列
int dp[MAX_N][MAX_N];
int n,m,s[MAX_N];

void addedge(int u,int v){  //根據鄰接表建樹的過程
    ver[++tot] = v;
    nxt[tot] = head[u];
    head[u] = tot;
}

void dfs(int u){
    dp[u][0] = 0;
    for(int i = 1;i < m;i++)  dp[u][i] = s[u];  //初始化都加上本身的學分
    for(int i = head[u];i;i = nxt[i]){
        int v = ver[i];
        dfs(v);
        for(int j = m;j >= 1;j--){  //逆序列舉揹包容量
            for(int k = j - 1;k >= 0;k--){  //列舉給子樹分配多少容量
                dp[u][j] = max(dp[u][j],dp[v][k] + dp[u][j-k]);     //子樹選k門課獲得的學分加上本身選j-k門課獲得的學分
            }
        }
    }
}

int main(){
	cin >> n >> m;
	int u;
	for(int i=1;i <= n;i++){
        cin >> u >> s[i];
        addedge(u,i);
	}
	m++;   //將0這門虛擬課程也算進去了
	dfs(0);
	cout << dp[0][m] ;
	return 0;
}

作者:inss!w! 出處:https://www.cnblogs.com/Hfolsvh/ 版權宣告:本部落格所有文章除特別宣告外,均採用 BY-NC-SA 許可協議。轉載請註明出處!