zoukankan      html  css  js  c++  java
  • BZOJ 1017: [JSOI2008]魔兽地图DotR

    传送门

    看一下题意,树上DP?

    然后考虑状态,设 f [ x ] [ k ] 表示在 x 的子树上花费 k 元时获得的最大价值

    但是可能当前买的用于更高一层的合成,如果这样不能确定当前买的东西是否够更高一层的合成

    所以多一维,设 f [ x ] [ j ] [ k ] ,x,k 意义同上,j 表示装备 x 留了 j 件用于高层合成(此时这 j 件 x 装备还不计入贡献)

    然后考虑转移,把子树状态一个个合并进入 f [ x ]

    但是发现合并时的 f 表示的东西不是我们设的东西,很不好转移,而且枚举 j 并不能确定当前总共要合成几个 x

    所以要枚举 i 表示当前总共合成 i 个 x 装备

    考虑设 g [ tot ] [ k ],表示在保证合成了 i 个 x 装备的情况下,当前枚举到第 tot 个儿子花费 k 元能获得的最大价值

    那么容易得到一个转移方程: $g[tot][k]=max(g[tot][k],g[tot-1][k-l]+f[v][w][l])$ (好像就是背包)

    $l,w$ 分别为 当前儿子的花费和当前儿子要上交的装备数量,初始 g 为 -INF,g [ 0 ] [ 0 ] = 0

    容易发现我们的 g 可以用滚动数组滚掉一维 tot,那就滚掉吧

    注意不管是 g 还是 f ,如果不存在方案那么值为 -INF

    最后再用 g 来更新 f ,枚举上交数量 j ,总花费 k,设val[ x ] 表示装备 x 的贡献 :

    那么$f[x][j][k]=max(f[x][j][k],g[k]+val[x]*(i-j))$,同样初始 f 为-INF

    注意上述转移是高级装备 x 的转移,基本装备就直接搞就好了,很简单

    注意最后可能是森林,所以要再设一个 h [ k ] 然后对每个树根做一个简单背包

    转移不用写了吧

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    typedef unsigned long long ull;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=57,M=2007,INF=1e9+7;
    int fir[N],from[M],to[M],num[M],cntt;
    inline void add(int &a,int &b,int &c)
    {
        from[++cntt]=fir[a]; fir[a]=cntt;
        to[cntt]=b; num[cntt]=c;
    }
    int n,m;
    int p[N],cst[N],val[N],mx[N];
    int f[N][N<<1][M],g[M];
    void dfs(int x)
    {
        if(p[x])//如果是基本装备
        {
            mx[x]=min(mx[x],m/cst[x]);//先算一下最大购买数量
            for(int i=0;i<=mx[x];i++)//上交几个
                for(int j=i;j<=mx[x];j++)/*买了几个*/ f[x][i][j*cst[x]]=val[x]*(j-i);
            return;
        }
        mx[x]=INF;
        for(int i=fir[x];i;i=from[i])//先更新子树
        {
            int &v=to[i]; dfs(v);
            cst[x]+=cst[v]*num[i];
            mx[x]=min(mx[x],mx[v]/num[i]);//更新最大购买数量
        }
        mx[x]=min(mx[x],m/cst[x]);//更新最大购买数量
        for(int i=0;i<=mx[x];i++)//合成几个
        {
            memset(g,~0x3f,sizeof(g)); g[0]=0;
            for(int j=fir[x];j;j=from[j])//先把每个儿子并到g里
            {
                int &v=to[j],w=num[j]*i;
                for(int k=m;k>=0;k--)//总花费,注意倒着枚举
                {
                    int t=-INF;//注意如果f[v][w][l]全都不合法的话g[k]也不可能合法,为-INF
                    for(int l=0;l<=k;l++)/*在当前儿子子树上花费*/ t=max(t,g[k-l]+f[v][w][l]);
                    g[k]=t;
                }
            }
            for(int j=0;j<=i;j++)//上交几个
                for(int k=0;k<=m;k++)/*总共用了多少钱*/ f[x][j][k]=max(f[x][j][k],g[k]+val[x]*(i-j));
        }
    }
    int h[M];
    bool pd[N];
    int main()
    {
        memset(f,~0x3f,sizeof(f));
        n=read(); m=read();
        char ch[5]; int a,b,c;
        for(int i=1;i<=n;i++)
        {
            val[i]=read(); scanf("%s",ch);
            if(ch[0]=='A')
            {
                a=read();
                for(int j=1;j<=a;j++)
                    b=read(),c=read(),add(i,b,c),pd[b]=1;//pd判断是否有父节点
            }
            else cst[i]=read(),mx[i]=read(),p[i]=1;//p判断是否是基础装备
        }
        for(int i=1;i<=n;i++)//可能是森林
        {
            if(pd[i]) continue;//只要考虑树根
            dfs(i);
            for(int j=m;j>=0;j--)//总共用了多少钱,注意倒着枚举
                for(int k=0;k<=j;k++)/*砸了多少钱到这个树里*/ h[j]=max(h[j],h[j-k]+f[i][0][k]);//就是基础背包
        }
        int ans=0;
        for(int i=0;i<=m;i++) ans=max(ans,h[i]);
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    C语言中static修饰符的意义
    网络字节序
    socket接口详解
    Python实现常见算法[3]——汉罗塔递归
    Python实现常见算法[2]——快速排序
    Python实现常见算法[1]——冒泡排序
    关于钛星设备主动推送报文的解析
    2015自我提升计划
    python 基础(函数 命名空间和作用域,嵌套和作用域链,闭包)
    python 基础(文件操作,注册,以及函数)
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10118488.html
Copyright © 2011-2022 走看看