[JSOI2008]魔兽地图

对于每一个子树x,我们有

f[x][i][k] 表示当前子树为x,有i个物品上交上一层合成(从而不计贡献但是计入成本),子树内一共投入了k元的最大收益

一个子树的贡献有以下几个方面:

1.其余儿子节点内部的贡献

2.儿子节点上交的物品合成一部分当前节点物品,这一部分又有一部分被截留在当前节点,产生了贡献

那么影响一个节点决策的因素有以下几个方面:

1.投入

2.合成几个

3.上交几个

其中1和3会影响父节点的决策,而2仅仅会影响子节点的决策,所以先枚举2,再讨论在多种情况2之下哪种1和3的组合最优

当情况2确定的时候,相当于在做多重背包,而背包的条件因为2改变而改变,因而我们需要用g数组来暂存答案,用以更新f

最后再用h数组对于f数组做一个多重背包,因为可能是一个森林.
//made by boboyang 
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define mem(Arr,x) memset(Arr,x,sizeof(Arr))
const int maxN=60;
const int maxM=maxN*maxN*4;
const int maxW=2010;
const int maxL=201;
const int inf=47483647;
class Edge
{
public:
   int v,cnt;
};
int n,m;
int edgecnt=0,Head[maxN],Next[maxM],Degree[maxN];
Edge E[maxM];
int Limit[maxN],Cost[maxN],W[maxN];
bool Leaf[maxN];
int F[maxN][maxL][maxW];
int G[maxN][maxW],H[maxN][maxW];
void Add_Edge(int u,int v,int cnt);
void dfs(int u);
int main()
{
   ios::sync_with_stdio(false);
   mem(Head,-1);mem(F,-0x3f3f3f3f);
   cin>>n>>m;
   for (int i=1;i<=n;i++)
   {
   	char opt;cin>>W[i]>>opt;
   	if (opt=='A')
   	{
   		int C;cin>>C;
   		while (C--)
   		{
   			int v,cnt;cin>>v>>cnt;
   			Add_Edge(i,v,cnt);
   			Leaf[i]=0;
   		}
   	}
   	if (opt=='B')
   	{
   		cin>>Cost[i]>>Limit[i];
   		Limit[i]=min(Limit[i],m/Cost[i]);
   		Leaf[i]=1;
   	}
   }
   int tot=0;
   for (int node=1;node<=n;node++)
   	if (Degree[node]==0)
   	{
   		dfs(node);
   		tot++;
   		for (int i=0;i<=m;i++)//枚举花多少钱
   			for (int j=0;j<=i;j++)//枚举这i元钱中有多少从上一次转移过来
   				for (int k=0;k<=Limit[node];k++)//枚举这一次node号选择合成多少个
   					H[tot][i]=max(H[tot][i],H[tot-1][j]+F[node][k][i-j]);
   	}
   int Ans=0;
   for (int i=0;i<=m;i++) Ans=max(Ans,H[tot][i]);
   cout<<Ans<<endl;
   return 0;
}
void Add_Edge(int u,int v,int cnt)
{
   edgecnt++;Next[edgecnt]=Head[u];Head[u]=edgecnt;E[edgecnt].v=v;E[edgecnt].cnt=cnt;
   Degree[v]++;
   return;
}
void dfs(int u)
{
   //cout<<"u:"<<u<<endl;
   if (Leaf[u]==1)//叶子节点
   {
   	//cout<<"Leaf:"<<u<<endl;
   	for (int i=0;i<=Limit[u];i++)//枚向上贡献多少个
   		for (int j=i;j<=Limit[u];j++)//枚举总共多少个,那么j-i就是自己保留的个数
   			F[u][i][j*Cost[u]]=W[u]*(j-i);
   	return;
   }
   Limit[u]=inf;
   for (int i=Head[u];i!=-1;i=Next[i])
   {
   	dfs(E[i].v);
   	Cost[u]+=Cost[E[i].v]*E[i].cnt;
   	Limit[u]=min(Limit[u],Limit[E[i].v]/E[i].cnt);
   }
   Limit[u]=min(Limit[u],m/Cost[u]);
   //for (int i=0;i<maxN;i++) for (int j=0;j<maxW;j++) G[i][j]=-inf;
   mem(G,-0x3f3f3f3f);
   G[0][0]=0;
   for (int l=Limit[u];l>=0;l--)
   {
   	int tot=0;
   	for (int e=Head[u];e!=-1;e=Next[e])
   	{
   		tot++;
   		for (int i=0;i<=m;i++)//枚举这一次总共花多少钱
   			for (int j=0;j<=i;j++)//枚举从这一棵子树转移过来多少钱,那么i-j就是从前面的子树转移过来的
   				G[tot][i]=max(G[tot][i],G[tot-1][i-j]+F[E[e].v][l*E[e].cnt][j]);
   	}
   	for (int i=0;i<=l;i++)
   		for (int j=0;j<=m;j++)
   			F[u][i][j]=max(F[u][i][j],G[tot][j]+(l-i)*W[u]);
   }
   return;
}
原文地址:https://www.cnblogs.com/zi-nai-boboyang/p/11437193.html