1. 程式人生 > >[題解]P2014 選課-樹型依賴的揹包

[題解]P2014 選課-樹型依賴的揹包

題目連結:https://www.luogu.org/problemnew/show/P2014

 

題目描述

 

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

 

輸入輸出格式

輸入格式:

 

第一行有兩個整數N,M用空格隔開。(1<=N<=300,1<=M<=300)

 

接下來的N行,第I+1行包含兩個整數ki和si, ki表示第I門課的直接先修課,si表示第I門課的學分。若ki=0表示沒有直接先修課(1<=ki<=N, 1<=si<=20)。

輸出格式:

 

只有一行,選M門課程的最大得分。

 

解題思路:

f[i][j][k]表示以i為根的子樹,前j個結點,選了k個結點的最大得分。

這個狀態就是有樹型依賴的揹包問題。

考慮優化狀態,f[i][j]表示以i為根的子樹,選了k個結點的最大得分。

這是0/1揹包從2維優化到1維的思路。

還可以用滾動陣列.

程式碼:

#include<iostream>
#include
<cstdio> #include<queue> #include<cstring> #include<cmath> #include<algorithm> #define R register #define ll long long int using namespace std; const int N=308; int n,m,sco[N],f[N][N],num,head[N],fa[N]; struct E{ int nxt,to; }e[N<<2]; inline void add(R int
u,R int v) { e[++num].nxt=head[u]; e[num].to=v; head[u]=num; } inline void dfs(R int pos,R int fa) { for(R int i=head[pos];i;i=e[i].nxt) { R int v=e[i].to; if(v!=fa) { dfs(v,pos); for(R int j=m;j>=0;j--)//列舉總數 for(R int k=0;k<=j;k++)//列舉選多少 f[pos][j]=max(f[pos][j],f[pos][j-k]+f[v][k]); } } if(pos!=0) for(R int i=m;i>=1;i--) f[pos][i]=f[pos][i-1]+sco[pos]; } int main(){ scanf("%d%d",&n,&m); for(R int i=1;i<=n;i++) { R int v; scanf("%d%d",&v,&sco[i]); add(v,i); add(i,v); } dfs(0,-1); printf("%d",f[0][m]); return 0; }