HZOI 可怜与超市 树形dp

学长留的题,质量还是灰常高的。

而且我树规本身较弱,一道也不想放下

题目链接:https://www.cnblogs.com/Juve/articles/11203824.html

题解:这道题我们可以看出是一个树形结构,而且是dp

dp首先要定义状态

设$dp[i][j][0/1]$表示当前考虑到第i个物品(以i为根的子树中),买了j个物品的代价,0表示不用i的优惠券,1表示使用

初始状态:$dp[i][1][1]=c[i]-d[i],dp[i][1][0]=c[i],dp[i][0][0]=0$,其他都是最大值

目标:$ans=max(i),(dp[1][i][1]<=b)$

转移:  $dp[x][j+k][1]=min(dp[x][j][1]+min(dp[y][k][0],dp[y][k][1]))$

    $dp[x][j+k][0]=min(dp[x][j][0]+dp[y][k][0])$

x表示当前节点,y表示当前搜索到的x的儿子,j是枚举的当前x的size,k是枚举的y的size

方程的话,你看看它代表什么就能理解了

有人说要特判k=0时的情况,因为如果你用优惠券,就必须买这个产品,所以没有dp[i][0][1]这种情况

但其实初始状态中我们把它设成了最大值,所以不会对结果有影响

上代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 5005
#define ll long long
using namespace std;
ll n,b,c[MAXN],d[MAXN],x[MAXN],ans=0;
ll to[MAXN<<1],nxt[MAXN<<1],pre[MAXN],cnt=0;
void add(ll u,ll v){
	cnt++,to[cnt]=v,nxt[cnt]=pre[u],pre[u]=cnt;
}
int size[MAXN],dp[MAXN][MAXN][2];//dp[i][j][0/1]:以i为子树,买了j个的价钱,0为不用券,1为用券,
void dfs(ll rt){
	size[rt]=1;
	dp[rt][1][1]=c[rt]-d[rt],dp[rt][1][0]=c[rt],dp[rt][0][0]=0;
	for(ll i=pre[rt];i;i=nxt[i]){
		ll y=to[i];
		dfs(y);
		for(ll j=size[rt];j>=0;j--){
			for(ll k=size[y];k>=0;k--){
				dp[rt][j+k][1]=min(dp[rt][j+k][1],dp[rt][j][1]+min(dp[y][k][0],dp[y][k][1]));
				dp[rt][j+k][0]=min(dp[rt][j+k][0],dp[rt][j][0]+dp[y][k][0]);
			}
		}
		size[rt]+=size[y];
	}
}
int main(){
	memset(dp,0x3f,sizeof(dp));
	scanf("%lld%lld",&n,&b);
	scanf("%lld%lld",&c[1],&d[1]);
	for(ll i=2;i<=n;i++){
		scanf("%lld%lld%lld",&c[i],&d[i],&x[i]);
		add(x[i],i);
	}
	dfs(1);
	for(ll i=1;i<=n;i++){
		if(dp[1][i][1]<=b)
			ans=i;
	}
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/Juve/p/11203774.html