Acwing 286.選課 (樹上依賴揹包)
題目
學校實行學分制。
每門的必修課都有固定的學分,同時還必須獲得相應的選修課程學分。
學校開設了 N 門的選修課程,每個學生可選課程的數量 M 是給定的。
學生選修了這 M 門課並考核通過就能獲得相應的學分。
在選修課程中,有些課程可以直接選修,有些課程需要一定的基礎知識,必須在選了其他的一些課程的基礎上才能選修。
例如《Windows程式設計》必須在選修了《Windows操作基礎》之後才能選修。
我們稱《Windows操作基礎》是《Windows程式設計》的先修課。
每門課的直接先修課最多隻有一門。
兩門課可能存在相同的先修課。
你的任務是為自己確定一個選課方案,使得你能得到的學分最多,並且必須滿足先修條件。
假定課程之間不存在時間上的衝突。
輸入格式
輸入檔案的第一行包括兩個整數N、M(中間用一個空格隔開)其中1≤N≤300,1≤M≤N。
接下來N行每行代表一門課,課號依次為1,2,…,N。
每行有兩個數(用一個空格隔開),第一個數為這門課先修課的課號(若不存在先修課則該項為0),第二個數為這門課的學分。
學分是不超過10的正整數。
輸出格式
輸出一個整數,表示學分總數。
輸入樣例:
7 4
2 2
0 1
0 4
2 1
7 1
7 6
2 2
輸出樣例:
13
思路
這個問題和那道上司的舞會很像,但又很不一樣。其一,這個問題裡面,我們不只有一棵樹,所有的依賴關係可能是由森林的形式給出,其二,這些課之間的依賴關係如何處理。那麼對於第一個問題來說,我們可以利用一個圖論裡面常用的思路,建一個超級源點,然後只需要求0點的dp值就好了。對於第二個點的話,我們可以把一個點下懸掛的n棵子樹看做是n組的揹包,每組揹包裡面的物品體積是1到m-1,然後我們做一次分組揹包,但是要注意做揹包的時候要在總體積上減去1,保留父親的位置,以保障依賴關係。
程式碼實現
#include<cstdio> #include<algorithm> #include<vector> #include<queue> #include<map> #include<iostream> #include<cstring> #include<cmath> using namespace std; #define rep(i,f_start,f_end) for (int i=f_start;i<=f_end;++i) #define per(i,n,a) for (int i=n;i>=a;i--) #define MT(x,i) memset(x,i,sizeof(x) ) #define rev(i,start,end) for (int i=0;i<end;i++) #define inf 0x3f3f3f3f #define mp(x,y) make_pair(x,y) #define lowbit(x) (x&-x) #define MOD 1000000007 #define exp 1e-8 #define N 1000005 #define fi first #define se second #define pb push_back typedef long long ll; typedef pair<int ,int> PII; typedef pair<int ,PII> PIII; ll gcd (ll a,ll b) {return b?gcd (b,a%b):a; } inline int read() { char ch=getchar(); int x=0, f=1; while(ch<'0'||ch>'9') { if(ch=='-') f = -1; ch=getchar(); } while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*f; } const int maxn=310; int n,m; vector <int > v[maxn]; int w[maxn]; int f[maxn][maxn]; //f[root][m] void dfs (int u) { rev (i,0,v[u].size ()) { int son=v[u][i]; dfs (son); per (j,m-1,0) { rep (k,1,j) { f[u][j]=max (f[u][j],f[u][j-k]+f[son][k]); } } } per (i,m,1) { f[u][i]=f[u][i-1]+w[u]; } f[u][0]=0; } int main () { cin>>n>>m; rep (i,1,n) { int a; cin>>a>>w[i]; v[a].pb (i); } m++; dfs (0); cout<<f[0][m]<<endl; return 0; }