試題描述:ios
大學實行學分制。每門課程都有必定的學分,學生只要選修了這門課並經過考覈就能得到相應學分。學生最後的學分是他選修各門課的學分總和。
每一個學生都要選擇規定數量的課程。其中有些課程能夠直接選修,有些課程須要必定的基礎知識,必須在選了其餘的一些課程基礎上才能選修。例如《數據結構》必須在選修了《高級語言程序設計》後才能選修。咱們稱《高級語言程序設計》是《數據結構》的先修課。每門課的直接先修課最多隻有一門。兩門課也可能存在相同的先修課。爲便於表述每門課都有一個課號,課號依次爲 1,2,3…
上例中課號1是課號2的先修課,即若是要先修課號2,則課號1一定已被選過。一樣,若是要選修課號3,那麼課號1和課號2都必定被選修過。
學生不可能學完大學開設的全部課程,所以必須在入學時選定本身要學的課程。每一個學生可選課程的總數是給定的。如今請你找出一種選課方案,使得你能獲得的學分最多,而且必須知足先修課優先的原則。假定課程間不存在時間上的衝突。git
首先能一眼看出來,這些課程是一個樹形結構,對於每個節點i,都須要判斷它的全部兒子是否取,又往下取多少。數據結構
這樣咱們把問題轉化成了一個分組揹包:對於每個節點,每一顆子樹就是一組,每一組又m(也就是選課數)個物品,第i物品的體積是i,價值是f[j][i](j爲當前兒子)spa
而後轉移方程就能夠變爲f[i][j][k]表示在以i爲根節點的子樹中,第j組,取k個物品的最優值。設計
易獲得:f[i][j][k]=max(f[i][j][k],f[i][j-1][k-q](q爲枚舉物品)+f[i][y][q])(y爲子節點)code
而後,咱們發現能夠把j的那一位壓去,可是注意循環要倒着寫。blog
#include <iostream> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cstring> #define MAXN 1010 #define in(a) a=read() #define REP(i,k,n) for(int i=k;i<=n;i++) using namespace std; inline int read(){ int x=0,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0'; return x*f; } int n,m; int total,head[MAXN],to[MAXN<<1],nxt[MAXN<<1],f[MAXN][MAXN],s[MAXN]; inline void adl(int a,int b){ total++; to[total]=b; nxt[total]=head[a]; head[a]=total; return ; } inline void dfs(int i){ for(int e=head[i];e;e=nxt[e]){ dfs(to[e]); for(int j=m;j>=0;j--) REP(k,0,j) f[i][j]=max(f[i][j],f[i][j-k]+f[to[e]][k]); } if(i) for(int j=m;j>0;j--) f[i][j]=f[i][j-1]+s[i]; return ; } int main(){ in(n),in(m); int a,b; REP(i,1,n) in(a),in(s[i]),adl(a,i); dfs(0); cout<<f[0][m]; return 0; }