bzoj1017: [JSOI2008]魔兽地图

本文介绍了一个复杂的树形动态规划问题,通过定义状态f[i][j][k]来解决子节点向父节点上缴物品的问题,并考虑了物品获取上限及森林情况下的处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

一个超级奇怪的树形dp。因为儿子管父亲

思路是显然的:f[i][j][k],表示第I个物品,用于合成(上缴)的有J个,花费K的代价能获得的最大价值。

我们还可以求出物品的获得上限(没钱or没子物品)

接下来就是一个背包了。

最外层枚举当前物品取几个

里面枚举儿子,用M^2时间可以跑出背包。

辅助数组G[i][j]前i个儿子花费j的代价能得到的最优值

然后枚举私吞的个数。

但是,数据中有森林的情况。

必须要加一个虚根,或者跑一个类似于G数组的dp

因此无脑贡献3WA


 
#include<cstdio>  #include<iostream> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #include<map> #define inf 0x3f3f3f3f using namespace std; struct edge{int to,next,v;}e[20005]; int a[55],b[55],p[55],f[55][105][2005],g[55][2005],head[55],deg[55]; char ch[5]; int n,m,x,y,z,tot,ans,cnt,h[55][2005]; void ins(int u,int v,int w){ e[++tot].to=v; e[tot].v=w; e[tot].next=head[u]; head[u]=tot; deg[v]++; } void dp(int x){ if (!head[x]){ b[x]=min(b[x],m/a[x]); for (int i=0;i<=b[x];i++) for (int j=i;j<=b[x];j++) f[x][i][j*a[x]]=(j-i)*p[x]; return; } b[x]=inf; for (int i=head[x];i;i=e[i].next){ dp(e[i].to); b[x]=min(b[x],b[e[i].to]/e[i].v); a[x]+=e[i].v*a[e[i].to]; } b[x]=min(b[x],m/a[x]); memset(g,233,sizeof(g)); g[0][0]=0; for (int l=b[x];l>=0;l--){ int tot=0; for (int i=head[x];i;i=e[i].next){ tot++; for (int j=0;j<=m;j++) for (int k=0;k<=j;k++) g[tot][j]=max(g[tot][j],g[tot-1][j-k]+f[e[i].to][l*e[i].v][k]); } for (int j=0;j<=l;j++) for (int k=0;k<=m;k++) f[x][j][k]=max(f[x][j][k],g[tot][k]+p[x]*(l-j)); } } int main(){ memset(f,233,sizeof(f)); memset(h,233,sizeof(h)); h[0][0]=0; scanf("%d%d",&n,&m); for (int i=1;i<=n;i++){ scanf("%d%s",&p[i],ch); if (ch[0]=='A'){ scanf("%d",&x); while (x--){ scanf("%d%d",&y,&z); ins(i,y,z); } } else scanf("%d%d",&a[i],&b[i]); } for (int x=1;x<=n;x++) if (!deg[x]){ dp(x); cnt++; for (int i=0;i<=m;i++) for (int j=0;j<=i;j++) for (int k=0;k<=b[x];k++) h[cnt][i]=max(h[cnt][i],h[cnt-1][j]+f[x][k][i-j]); } ans=0; for (int i=0;i<=m;i++) ans=max(ans,h[cnt][i]); printf("%d\n",ans); }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值