[oiclass1454]選課:樹上揹包
阿新 • • 發佈:2022-01-11
題目
在大學裡每個學生,為了達到一定的學分,必須從很多課程裡選擇一些課程來學習,在課程裡有些課程必須在某些課程之前學習,如高等數學總是在其它課程之前學習。現在有 \(N\) 門功課,每門課有個學分,每門課有一門或沒有直接先修課(若課程 \(a\) 是課程 \(b\) 的先修課即只有學完了課程 \(a\),才能學習課程 \(b\))。一個學生要從這些課程裡選擇 \(M\) 門課程學習,問他能獲得的最大學分是多少?
輸入
第一行有兩個整數 \(N,M\) 用空格隔開。(\(1\leq N \leq 300,1\leq M\leq 200\))
接下來的 \(N\) 行,第 \(i+1\) 行包含兩個整數 \(k_i\)
輸出
只有一行,選 \(M\) 門課程的最大得分。
輸入樣例
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
輸出樣例
13
題解
經典題,傳統解法是將講多叉樹轉化成二叉樹,然後在二叉樹上進行樹形DP,現在使用樹上揹包來解會更簡單。
需要事先學習樹上揹包,理解分組揹包的求法,理解如何將問題轉化成樹上揹包問題。
本題有兩種定義方式。
1
定義\(f[i][j]\)
點選檢視程式碼
#include<bits/stdc++.h> using namespace std; const int N=300+5; vector<int> g[N]; int n,m,u,v,w,f[N][N],s[N]; void dfs(int u){ for(int i=1;i<=m;i++)f[u][i]=s[u]; for(int i=0;i<g[u].size();i++){ int v=g[u][i]; dfs(v); for(int j=m;j>=0;j--){ for(int k=0;k<j;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,&s[i]); g[x].push_back(i); } m++; dfs(0); printf("%d",f[0][m]); }
2
定義\(f[i][j]\)表示以i為根選擇j門課程的最大得分(不包含i的結點),程式碼如下:
點選檢視程式碼
#include<iostream>
#include<vector>
using namespace std;
vector<int> g[301];
int n,m,x,s[301],f[301][201];
void dfs(int u){
for(int i=0;i<g[u].size();i++){
int v=g[u][i];
dfs(v);
for(int j=m;j>=0;j--){
for(int k=0;k<j;k++){
f[u][j]=max(f[u][j],f[u][j-k-1]+f[v][k]+s[v]);
}
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>x>>s[i];
g[x].push_back(i);
}
dfs(0);
cout<<f[0][m];
}