[JSOI2008]魔兽地图

时间:2019-08-30
本文章向大家介绍[JSOI2008]魔兽地图,主要包括[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