標籤:set 背包 mes and 樹形背包 固定 eof span 題目
Description
DotR裡面的英雄只有一個屬性——力量。
他們需要購買裝備來提升自己的力量值,每件裝備都可以使佩戴它的英雄的力量值提高固定的點數,所以英雄的力量值等於它購買的所有裝備的力量值之和。
裝備分為基本裝備和進階裝備兩種。基本裝備可以直接從商店裡面用金幣購買,而進階裝備需要用基本
裝備或者較低級的進階裝備來合成,合成不需要附加的金幣。裝備的合成路線可以用一棵樹來表示。
比如,Sange and Yasha的合成需要Sange,Yasha和Sange and Yasha Recipe Scroll三樣物品。其中Sange又要用Ogre Axe, Belt
of Giant Strength和 Sange Recipe Scroll合成。
每件基本裝備都有數量限制,這限制了你不能無限制地合成某些性價比很高的裝備。
現在,英雄Spectre有M個金幣,他想用這些錢購買裝備使自己的力量值盡量高。你能幫幫他嗎?他會教你魔法Haunt(幽靈附體)作為回報的。
(1 <= n <= 51) 和 m (0 <= m <= 2,000)
Solution
一道擱置了很久的神題。
一看過去,一棵樹形合成路線,子樹的選擇與能否合成根有關,而且要分配一個金幣,最終獲得最高收益。而且還有物品的限制。
所以,就是一道樹形依賴背包題目了。
但是狀態不是很好設,因為子樹根的裝備可能留下,也可能等著合成更進階的裝備。
所以狀態中必須要記錄i根節點的子樹,合成多少個i要用於上面的合成
設f[i][j][k]表示,以i為根的子樹,合成j個i用於上面的合成,總共花費k元錢,也就是購買葉子花費k元。
轉移的時候,
先把每個子樹的答案算出來。
回溯到x後,外層枚舉l表示合成幾個x
然後依次選擇每個子樹,用樹形背包。
注意,這裡每個子樹都要選擇合成至少l*need[y]個,need[x]表示x合成一個父親所需要的個數。
所以,不能像一般的背包,每個子樹都要選擇。
用分組背包,g[tot][j]表示,考慮了前tot個子樹,花費j元錢,得到的最大力量。(每個子樹都滿足至少有l*need[y])個
g[tot][j]=max(g[tot-1][j-k]+f[y][l*nd[y]][k])
統計完了之後,
再枚舉一個j,表示,l中留下j個合成x上一層的裝備。
f[x][j][k]=max(g[tot][k]+(l-j)*P[x])
要注意的是,為了保證用了l*nd[y]個,必須令g,f初值是-inf
0肯定是不行的。那就可能會用少於l*nd[y]的錢就合成了l*nd個,雖然總力量是0,但是也可能是一個最優解。
有的時候,為了轉移合法,必須把初值設定為極大或者極小值。
這樣,每次的最優解,就必定會從這裡出來。
可以順便dp一下合成每個x所需要的價值,以及x合成的上限,可以減少迴圈的長度。
Code
#include<bits/stdc++.h>using namespace std;typedef long long ll;const int M=2000+3;const int N=55;const int inf=0x3f3f3f3f;int n,m;int L[N],P[N],C[N];int nd[N];bool ba[N];struct node{ int nxt,to;}e[2*N];int hd[N],cnt;void add(int x,int y){ e[++cnt].nxt=hd[x]; e[cnt].to=y; hd[x]=cnt;}void dp(int x){ if(ba[x]){ L[x]=min(L[x],m/C[x]); return; } for(int i=hd[x];i;i=e[i].nxt){ int y=e[i].to; dp(y); C[x]+=C[y]*nd[y]; L[x]=min(L[x],L[y]/nd[y]); } L[x]=min(L[x],m/C[x]); }int f[N][105][M];int g[N][M];int rt;bool du[N];void dfs(int x){ if(ba[x]){ for(int l=0;l<=L[x];l++){ for(int j=0;j<=l;j++){ f[x][j][l*C[x]]=P[x]*(l-j); } } return ; } for(int i=hd[x];i;i=e[i].nxt){ int y=e[i].to; dfs(y); } for(int l=0;l<=L[x];l++){ int now=0; for(int i=hd[x];i;i=e[i].nxt){ int y=e[i].to; now++; memset(g[now],-0x3f,sizeof g[now]); for(int j=0;j<=m;j++){ for(int k=0;k<=j;k++){ g[now][j]=max(g[now][j],g[now-1][j-k]+f[y][l*nd[y]][k]); } } } for(int h=0;h<=l;h++){ for(int k=0;k<=m;k++){ if(g[now][k]+(l-h)*P[x]>f[x][h][k]) { f[x][h][k]=g[now][k]+(l-h)*P[x]; } } } }}int main(){ scanf("%d%d",&n,&m); char op;int s; memset(L,inf,sizeof L); for(int i=1;i<=n;i++){ scanf("%d ",&P[i]); op=getchar(); if(op==‘B‘){ ba[i]=1;//is a leaf du[i]=1; scanf("%d%d",&C[i],&L[i]); } else{ scanf("%d",&s); int son; for(int j=1;j<=s;j++){ scanf("%d",&son); scanf("%d",&nd[son]); du[son]=1; add(i,son); } } } for(int i=1;i<=n;i++) if(!du[i]) rt=i; dp(rt); memset(f,-inf,sizeof f); dfs(rt); int ans=0; for(int j=0;j<=L[rt];j++){ for(int k=0;k<=m;k++){ ans=max(ans,f[rt][j][k]); } } printf("%d",ans); return 0;}
[JSOI2008]魔獸地圖