[BZOJ1017][JSOI2008]魔兽地图DotR-动态规划

魔兽地图DotR

Description

  DotR (Defense of the Robots) Allstars是一个风靡全球的魔兽地图,他的规则简单与同样流行的地图DotA (Defense of the Ancients) Allstars。DotR里面的英雄只有一个属性——力量。他们需要购买装备来提升自己的力量值,每件装备都可以使佩戴它的英雄的力量值提高固定的点数,所以英雄的力量值等于它购买的所有装备的力量值之和。装备分为基本装备和高级装备两种。基本装备可以直接从商店里面用金币购买,而高级装备需要用基本装备或者较低级的高级装备来合成,合成不需要附加的金币。装备的合成路线可以用一棵树来表示。比如,Sange and Yasha的合成需要Sange,Yasha和Sange and Yasha Recipe Scroll三样物品。其中Sange又要用Ogre Axe, Belt of Giant Strength和 Sange Recipe Scroll合成。每件基本装备都有数量限制,这限制了你不能无限制地合成某些性价比很高的装备。现在,英雄Spectre有M个金币,他想用这些钱购买装备使自己的力量值尽量高。你能帮帮他吗?他会教你魔法Haunt(幽灵附体)作为回报的。

Input

  第一行包含两个整数,N (1 <= n <= 51) 和 m (0 <= m <= 2,000)。分别表示装备的种类数和金币数。装备用1到N的整数编号。
  接下来的N行,按照装备1到装备n的顺序,每行描述一种装备。
  每一行的第一个非负整数表示这个装备贡献的力量值。
  接下来的非空字符表示这种装备是基本装备还是高级装备,A表示高级装备,B表示基本装备。如果是基本装备,紧接着的两个正整数分别表示它的单价(单位为金币)和数量限制(不超过100)。如果是高级装备,后面紧跟着一个正整数C,表示这个高级装备需要C种低级装备。后面的2C个数,依次描述某个低级装备的种类和需要的个数。

Output

  第一行包含一个整数S,表示最多可以提升多少点力量值。

Sample Input

10 59
5 A 3 6 1 9 2 10 1
1 B 5 3
1 B 4 3
1 B 2 3
8 A 3 2 1 3 1 7 1
1 B 5 3
5 B 3 3
15 A 3 1 1 5 1 4 1
1 B 3 5
1 B 4 3

Sample Output

33


在WA3+以后不甘心地随手交一个标程——WA???
又换了3个标称一交——全WA?
思考半小时后得出结论:存在只有低级装备的数据!
然后特判写了个多重背包。
最后,A了才发现讨论区里说新加了一组数据,一组只有低级装备的数据……
浪费时间啊啊啊啊

(╯‵□′)╯︵┻━┻


思路:
奇怪的树形DP。
那么首先这个树形DP是合并儿子答案去更新父亲节点的~
如何合并?
答:用背包思想。

f[i][j][k] 表示对于i号节点的装备,把其中j个用于合成父亲装备,花费了总共k的代价时的最大力量。

首先对于叶子节点,有dp方程:
f[i][j][kcost[u]]=(kj)val[i]
翻译:i号节点,购买k个,其中j个用于合成父亲所能达成的最大力量值为 (kj)val[i] ,也就是没有用于合成父亲的装备力量之和。

然后对于一种高级装备:
首先获取所有子节点的方案。
然后,令u为当前节点,v为某个子节点,得到DP方程:
f[u][j][k]=max(f[u][j][kr]+f[v][jneed[v]][r])
翻译为:授权k的金币给当前节点的情况下,使用r的金币去处理其v子树的最大力量。
这里的 need[v] 为合成单个u所需的v个数。

咱可以使用这个方程依次扫描所有子树,进行转移。
扫描时每次要把之前的 f[i][j][k] 存入一个 g[k] 数组中,用 g[k] 而不是 f[i][j][k] 更新新的 f[i][j][k] ,避免自己反复更新。

然后,到上面为止咱还没有考虑当前节点使用一些额外费用来保留一些当前装备,不用于合成而是参与总力量计算的情况。
那么内部多重背包转移:
f[u][i][k]=maxx(f[u][i][k],f[u][j][k]+(ji)val[u])
那么这就可以了!

然后,BZOJ加了一组只有低级装备的数据……
那么考虑直接多重背包即可~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>

using namespace std;

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || '9'<ch){if(ch=='-')f=-1;ch=getchar();}
    while('0'<=ch && ch<='9')x=x*10+(ch^48),ch=getchar();
    return x*f;
}

inline int minn(int a,int b){if(a<b)return a;return b;}
inline int maxx(int a,int b){if(a>b)return a;return b;}

const int N=89;
const int M=2011;
const int T=110;
const int Inf=2147483647;

int n,m,ans=-Inf;
int f[N][T][M];
int g[M],fa[N];
int val[N],cost[N],lim[N];
int to[M],nxt[M],need[N],beg[N],tot;

inline void add(int u,int v)
{
    to[++tot]=v;
    nxt[tot]=beg[u];
    beg[u]=tot;
}

void dfs(int u)
{
    if(!beg[u])
    {
        lim[u]=minn(lim[u],m/cost[u]);
        for(int i=0;i<=lim[u];i++)
            for(int j=i;j<=lim[u];j++)
                f[u][i][j*cost[u]]=(j-i)*val[u];

        return;
    }

    lim[u]=Inf;
    for(int i=beg[u],v;i;i=nxt[i])
    {
        dfs(v=to[i]);
        lim[u]=minn(lim[u],lim[v]/need[v]);
    }

    for(int i=0;i<=lim[u];i++)
        f[u][i][0]=0;

    for(int i=beg[u],v;i;i=nxt[i])
    {
        v=to[i];
        for(int j=0;j<=lim[u];j++)
        {
            memcpy(g,f[u][j],sizeof(f[u][j]));  
            memset(f[u][j],-1,sizeof(f[u][j]));

            for(int k=m;k>=0;k--)
                for(int r=k;r>=0;r--)
                    if(g[k-r]!=-1 && f[v][j*need[v]][r]!=-1)
                    {
                        f[u][j][k]=maxx(f[u][j][k],g[k-r]+f[v][j*need[v]][r]);
                        ans=maxx(ans,f[u][j][k]);
                    }
        }
    }

    for(int i=0;i<=lim[u];i++)
        for(int j=i;j<=lim[u];j++)
            for(int k=0;k<=m;k++)
                if(f[u][j][k]!=-1)
                {
                    f[u][i][k]=maxx(f[u][i][k],f[u][j][k]+(j-i)*val[u]);
                    ans=maxx(ans,f[u][i][k]);
                }
}

namespace plan_b
{
    int f[N];

    int main()
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=lim[i];j++)
                for(int k=m;k>=cost[i];k--)
                    f[k]=maxx(f[k],f[k-cost[i]]+val[i]);
        printf("%d\n",f[m]);
        return 0;
    }
}

int main()
{
    n=read();
    m=read();

    char ch[5];

    bool flag=0;
    for(int i=1;i<=n;i++)
    {
        val[i]=read();
        scanf("%s",ch);

        if(ch[0]=='A')
        {
            int c=read();
            for(int j=1,v,num;j<=c;j++)
            {
                v=read();
                num=read();
                add(i,v);
                need[v]=num;
                fa[v]=i;
            }
            flag=1;
        }
        else if(ch[0]=='B')
        {
            cost[i]=read();
            lim[i]=read();
        }
    }

    if(!flag)
        return plan_b::main();

    memset(f,-1,sizeof(f));
    for(int i=1;i<=n;i++)
        if(fa[i]==0)
            dfs(i);

    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值