洛谷P2014—選課(樹形DP)
阿新 • • 發佈:2021-07-17
題目
在大學裡每個學生,為了達到一定的學分,必須從很多課程裡選擇一些課程來學習,在課程裡有些課程必須在某些課程之前學習,如高等數學總是在其它課程之前學習。現在有 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]);
作者:inss!w! 出處:https://www.cnblogs.com/Hfolsvh/ 版權宣告:本部落格所有文章除特別宣告外,均採用 BY-NC-SA 許可協議。轉載請註明出處!#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; }