【2019/08/05测试 T1】药香沁鼻

传送门


problem

还剩 P P P 点能量值的小C开始用百色风铃熬中草药了,药香很快氤氲开来。

百色风铃一共有 n n n 朵花。为了能够简洁地描述这 n n n 朵花,小C给每一朵花都编上一个 1 1 1 ~ n n n 的唯一编号。百色风铃的每一朵花都是具有灵性的,这具体体现在以下三点:

  1. 摘下第 i i i 朵花需要损耗小C恰好 w i w_i wi 点能量。
  2. 每一朵花都有自己钦慕的花(有且仅有一朵),第 i i i 朵花钦慕的花用 f i f_i fi 表示(保证 1 ≤ f i ≤ m a x ( 1 , i − 1 ) 1 ≤ f_i ≤ max(1, i − 1) 1fimax(1,i1)),若在煮药时,第 i i i 朵花发现自己钦慕的第 f i f_i fi 朵花不在药中,则第 i i i 朵花会渐渐枯萎直至消失(即对药材的贡献为 0 0 0),这时若药材中有别的一些花朵钦慕第 i i i 朵花,则这些花也会枯萎并消失,以此类推,可能又有一部分花会枯萎并消失,这与一个连锁反应相当类似。
  3. 若将第 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 1n5×1031P,wi1041vi105


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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值