problem
还剩 P P P 点能量值的小C开始用百色风铃熬中草药了,药香很快氤氲开来。
百色风铃一共有 n n n 朵花。为了能够简洁地描述这 n n n 朵花,小C给每一朵花都编上一个 1 1 1 ~ n n n 的唯一编号。百色风铃的每一朵花都是具有灵性的,这具体体现在以下三点:
- 摘下第 i i i 朵花需要损耗小C恰好 w i w_i wi 点能量。
- 每一朵花都有自己钦慕的花(有且仅有一朵),第 i i i 朵花钦慕的花用 f i f_i fi 表示(保证 1 ≤ f i ≤ m a x ( 1 , i − 1 ) 1 ≤ f_i ≤ max(1, i − 1) 1≤fi≤max(1,i−1)),若在煮药时,第 i i i 朵花发现自己钦慕的第 f i f_i fi 朵花不在药中,则第 i i i 朵花会渐渐枯萎直至消失(即对药材的贡献为 0 0 0),这时若药材中有别的一些花朵钦慕第 i i i 朵花,则这些花也会枯萎并消失,以此类推,可能又有一部分花会枯萎并消失,这与一个连锁反应相当类似。
- 若将第 i i i 朵花放入药材中熬煮,并且这朵花没有消失的话,可以增加药材的价值 v i v_i vi 点(即对药材的贡献为 v i v_i vi)。
现在请你求出在最优解下,药材能获得的最大贡献是多少。
数据范围: 1 ≤ n ≤ 5 × 1 0 3 , 1 ≤ P , w i ≤ 1 0 4 , 1 ≤ v i ≤ 1 0 5 1 ≤ n ≤ 5 × 10^3,1 ≤ P, w_i ≤ 10^4,1 ≤ v_i ≤ 10^5 1≤n≤5×103,1≤P,wi≤104,1≤vi≤105。
solution
这是一类比较经典的问题。
当我们需要强制选根时,可以通过 d f s dfs dfs 序入手。
注意以下是按照 d f s dfs dfs 序枚举点。
不难发现这道题应该是一道树上背包的题,若用 f [ i ] [ j ] f[i][j] f[i][j] 表示在前 i i i 朵花中选能量值 ≤ j \le j ≤j 能获得的最大价值,则有:
f [ i + S i z e x ] [ j ] = m a x ( f [ i ] [ j ] , f [ i + S i z e x ] [ j ] ) f[i+Size_x][j]=max(f[i][j],f[i+Size_x][j]) f[i+Sizex][j]=max(f[i][j],f[i+Sizex][j])
这是不选 i i i 的情况,那么 i i i 的子树也不能选。
f [ i + 1 ] [ j + w x ] = m a x ( f [ i ] [ j ] + v x , f [ i + 1 ] [ j + w x ] ) f[i+1][j+w_x]=max(f[i][j]+v_x,f[i+1][j+w_x]) f[i+1][j+wx]=max(f[i][j]+vx,f[i+1][j+wx])
这是选 i i i 的情况。
其中 x x x 是在 d f s dfs dfs 序中 i i i 位置上的点。
那么按照这两个式子 d p dp dp即可,时间复杂度 O ( n P ) O(nP) O(nP)。
code
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define N 5005
#define M 10005
using namespace std;
int n,m,dfn;
int w[N],v[N],in[N],out[N],idx[N],f[N][M];
vector<int>e[N];
void dfs(int x){
in[x]=++dfn,idx[dfn]=x;
for(int i=0;i<e[x].size();++i) dfs(e[x][i]);
out[x]=dfn;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1,x;i<=n;++i){
scanf("%d%d%d",&w[i],&x,&v[i]);
if(i!=1) e[x].push_back(i);
}
dfs(1);
memset(f,128,sizeof(f));
memset(f[1],0,sizeof(f[1]));
for(int i=1;i<=n;++i){
int x=idx[i];
for(int j=0;j<=m;++j) f[out[x]+1][j]=max(f[out[x]+1][j],f[i][j]);
for(int j=0;j<=m-w[x];++j) f[i+1][j+w[x]]=max(f[i+1][j+w[x]],f[i][j]+v[x]);
}
printf("%d\n",f[n+1][m]);
return 0;
}