BZOJ1017&&洛谷P4037 [JSOI2008]魔兽地图

%一发hzwer大爷

树形背包dp

1.处理每个装备的上限--->普通装备总钱数除以单价,特殊装备--->子装备的上限除以需要的数量,最后再和总钱数/单价取min,单价是合成这个特殊装备的价值

2.定义f[i][j][k]表示第i件物品,j件用于向上传递,造这件装备一共花费了k时的最大贡献

3.对于以 x为根的子树,枚举合成的数量e,然后剩余的钱去买子树单独的装备

4.我们定义g[i][j]表示x的前i个子树,花费j的最大贡献

显然g[i][j]=max(g[i][j],g[i-1][j-now]+f[to][e*need][now])now是第i棵子树用的钱数,need是合成一件需要数量

5.然后用g去更新f[x][][]

6.假想一个根节点,把所有贡献更新到他身上

最后取个max

代码

//By ACerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=55;
const int inf=2e9;
int n,m,tot;
int f[M][M*2][M*M];
int c[M],on[M],lim[M];
int g[M][M*M],h[M][M*M],vis[M];
int to[M],nxt[M],head[M],w[M],cnt;
inline int read()
{
	int x=0;char ch=getchar();
	while (ch>'9'||ch<'0') ch=getchar();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}
inline void add(int x,int y,int z)
{
	to[++cnt]=y;nxt[cnt]=head[x];w[cnt]=z;head[x]=cnt;vis[y]=1;
	return ;
}
inline void getleaf(int x)
{
	lim[x]=min(lim[x],m/on[x]);//更新上限 
	for (int i=0;i<=lim[x];i++)
	for (int k=i;k<=lim[x];k++)
	f[x][i][k*on[x]]=(k-i)*c[x];//处理f数组,向上传递的装备没有单独贡献,所以是(k-i)个装备有贡献 
	return ;
}
inline void tdp(int x)
{
	if (!head[x]) return (void)(getleaf(x));
	lim[x]=inf;
	for (int i=head[x];i;i=nxt[i])
	{
		tdp(to[i]);
		lim[x]=min(lim[x],lim[to[i]]/w[i]);
		on[x]+=(w[i]*on[to[i]]);
	}
	lim[x]=min(lim[x],m/on[x]);
	memset(g,-0x3f,sizeof(g));g[0][0]=0;
	for (int i=lim[x];i>=0;i--)
	{
		int all=0;
		for (int k=head[x];k;k=nxt[k])
		{
			all++;
			for (int j=0;j<=m;j++)
			for (int e=0;e<=j;e++)
			g[all][j]=max(g[all][j],g[all-1][j-e]+f[to[k]][i*w[k]][e]); 
		}
		for (int k=0;k<=i;k++)
		for (int j=0;j<=m;j++)
		f[x][k][j]=max(f[x][k][j],g[all][j]+c[x]*(i-k));//和上面的普通装备的处理一样 
	}
	return ;
}  
signed main()
{
	n=read();m=read();char s[5];
	for (int i=1;i<=n;i++)
	{
		c[i]=read();scanf("%s",s);
		if (s[0]=='A')
		{
			int x=read(),y,z;
			while (x--) y=read(),z=read(),add(i,y,z);
		}
		else on[i]=read(),lim[i]=read();
	}
	memset(f,-0x3f,sizeof(f));int ans=0;
	for (int i=1;i<=n;i++)
	if (!vis[i])
	{
		tdp(i);tot++;
		for (int k=0;k<=m;k++)
		for (int j=0;j<=k;j++)
		for (int e=0;e<=lim[i];e++)
		h[tot][k]=max(h[tot][k],h[tot-1][j]+f[i][e][k-j]);
	}
	for (int i=0;i<=m;i++) 
	ans=max(ans,h[tot][i]);
	cout<<ans;
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值