[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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 有一个 $n$ 个点的棋盘,每个点上有一个数字 $a_i$,你需要从 $(1,1)$ 走到 $(n,n)$,每次只能往右或往下走,每个格子只能经过一次,路径上的数字和为 $S$。定义一个点 $(x,y)$ 的权值为 $a_x+a_y$,求所有满足条件的路径中,所有点的权值和的最小值。 输入格式 第一行一个整数 $n$。 接下来 $n$ 行,每行 $n$ 个整数,表示棋盘上每个点的数字。 输出格式 输出一个整数,表示所有满足条件的路径中,所有点的权值和的最小值。 数据范围 $1\leq n\leq 300$ 输入样例 3 1 2 3 4 5 6 7 8 9 输出样例 25 算法1 (树形dp) $O(n^3)$ 我们可以先将所有点的权值求出来,然后将其看作是一个有权值的图,问题就转化为了在这个图中求从 $(1,1)$ 到 $(n,n)$ 的所有路径中,所有点的权值和的最小值。 我们可以使用树形dp来解决这个问题,具体来说,我们可以将这个图看作是一棵树,每个点的父节点是它的前驱或者后继,然后我们从根节点开始,依次向下遍历,对于每个节点,我们可以考虑它的两个儿子,如果它的两个儿子都被遍历过了,那么我们就可以计算出从它的左儿子到它的右儿子的路径中,所有点的权值和的最小值,然后再将这个值加上当前节点的权值,就可以得到从根节点到当前节点的路径中,所有点的权值和的最小值。 时间复杂度 树形dp的时间复杂度是 $O(n^3)$。 C++ 代码 算法2 (动态规划) $O(n^3)$ 我们可以使用动态规划来解决这个问题,具体来说,我们可以定义 $f(i,j,s)$ 表示从 $(1,1)$ 到 $(i,j)$ 的所有路径中,所有点的权值和为 $s$ 的最小值,那么我们就可以得到如下的状态转移方程: $$ f(i,j,s)=\min\{f(i-1,j,s-a_{i,j}),f(i,j-1,s-a_{i,j})\} $$ 其中 $a_{i,j}$ 表示点 $(i,j)$ 的权值。 时间复杂度 动态规划的时间复杂度是 $O(n^3)$。 C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值