zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    二叉搜索树第k个节点
    序列化二叉树
    atoi()和stoi()函数
    02.规划过程组表格-风险数据表
    02.规划过程组表格-风险概率和影响评估
    02.规划过程组表格-风险登记册
    02.规划过程组表格-风险管理计划
    02.规划过程组表格-沟通管理计划
    02.规划过程组表格-人力资源管理计划
    02.规划过程组表格-责任分配矩阵
  • 原文地址:https://www.cnblogs.com/zi-nai-boboyang/p/11437193.html
Copyright © 2011-2022 走看看