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\n",ans); return 0; }