zoukankan      html  css  js  c++  java
  • [JSOI2008]魔兽地图DotR 做题心得

    [JSOI2008]魔兽地图DotR 做题心得

    马上要 JSOI2021 了,来看看以往的题

    对于树形dp题,就是要想一下:基本策略是什么?然后根据基本策略,想一想dp要维护什么,怎么转移。再加上一些套路之类的东西,维护一下。像本题用到的就是树上背包。

    还有就是,像树上花费多少价格购买甚么东西使得获利最大,长成这样的题,一般都是树上背包搞一搞。

    题解

    一些记号

    (n,m) 是物品数与总钱数

    我们定义 (pow(u)) 表示 (u) 的力量值,(pri(u)) 表示 (u) 的花费,(lim(u)) 表示 (u) 最多能搞多少个。

    对于非基础装备,(pri,lim) 并不是直接给,但是我们可以在树上算一下:

    (pri(u)) 就是它所需要的每个物品的价格乘以合成需要多少个物品,即

    [sumlimits_{vin son(u)} need(v)*pri(v) ]

    (lim(u)) 就是每个需要的物品的 (lim/need)(min),最后再和 (m/pri(u))(m) 是总钱数)取一下 (min)

    然后还有一些 dp 数组,后面再说

    基本策略与dp设状态

    我们肯定是,先买一堆基础物品,然后不断合上去

    对于中间的某些物品,我们用一部分增加力量值,并用剩下的一部分用来合成更高级的东西。注意我们并不一定全部用来合成,或者全部用来增加力量值。

    然后贯穿全题的,还有一个重要的东西:钱。钱是一切的根源,但是我们的钱有限,最重点要规划的就是钱怎么用。

    于是我们的dp就成型了:(f(u,j,k)) 表示 dp 到树上的点 (u),留 (j) 个传上去用来合成,现在花了 (k) 块钱,最大能获取的力量值。

    dp转移

    这个状态简单想一下大概是能转移的,考虑一下 (f(u,j,k)) 怎么转移

    我们枚举一共准备造 (J) 个 ((Jge j)),显然自己这里合成不需要花钱,所以钱((k) 块钱)全部花给儿子,假设这部分能获得的最大的力量值是 (P_{son})。然后贡献的力量值就是 (P_{son}+(J-j)*pow(u))

    (后面 (J-j) 是因为要留 (j) 个上传)

    现在的问题是 (P_{son}) 怎么算。假设每个儿子的 (f) 我们都能算出来,其实这就是一个合并背包的问题了,另开一个 dp 数组 (g) 来搞定这个问题。

    注:"背包合并"是我瞎口胡的一个说法,说着玩的,不知道它是否是一个专业的说法。

    (g[t][k]) 表示,考虑前 (t) 个儿子,花了 (k) 块钱,最大力量值。假设第 (t) 个儿子是 (v),那么 (v) 的义务是:上传 (j*need(v)) 个来给 (u) ,用来合成 (j)(u)。而它享有的权利是,获得 (k) 块钱中的一部分。

    于是我们枚举一下 (k) 块钱中分多少给它,得转移:

    [g[t][k]=g[t-1][k']+f(v,j*need(v),k-k') ]

    然后假设 (u) 的儿子总数为 (T),显然 (P_{son}) 就是 (g[T][k])

    到最后求完 (f) 之后,你会发现其实原树不连通,对于每个根都要求一遍,然后还要合并一下。

    但是其实这里的转移和 (g) 就几乎没区别了,除了没有责任义务的限制。抄着写,改改就行了。也是一个背包合并。

    总结

    有一个重要的思想是,我们固然要考虑当前怎么决策,但是也要为以后的决策做考虑:后面的决策需要用到哪些信息?要不要传上去?用什么方式传过去?这里联动一下 CF461E,也有类似的 “为后面的决策考虑” 的思想,只不过那个题传的信息过于简单了(

    我们联合!

    另一个重要的思想是,我们面对的问题和当前的局势非常的复杂,但是其实这个“局势”,只有少量的“特征值”,是我们关心的,而这就是我们的 (dp) 状态。比如我们虽然面临着复杂的分钱问题,但我们关心的只有:当前在哪,有多少钱,给上面多少。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    namespace Flandre_Scarlet
    {
        #define N 102
        #define M 2003
        #define F(i,l,r) for(int i=l;i<=r;++i)
        #define D(i,r,l) for(int i=r;i>=l;--i)
        #define Fs(i,l,r,c) for(int i=l;i<=r;c)
        #define Ds(i,r,l,c) for(int i=r;i>=l;c)
        #define MEM(x,a) memset(x,a,sizeof(x))
        #define FK(x) MEM(x,0)
        #define Tra(i,u) for(int i=G.st(u),v=G.to(i);~i;i=G.nx(i),v=G.to(i))
        #define p_b push_back
        #define sz(a) ((int)a.size())
        #define all(a) a.begin(),a.end()
        #define iter(a,p) (a.begin()+p)
        #define PUT(a,n) F(i,1,n) printf("%d ",a[i]); puts("");
        int I() {char c=getchar(); int x=0; int f=1; while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar(); while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return ((f==1)?x:-x);}
        template <typename T> void Rd(T& arg){arg=I();}
        template <typename T,typename...Types> void Rd(T& arg,Types&...args){arg=I(); Rd(args...);}
        void RA(int *p,int n) {F(i,1,n) *p=I(),++p;}
        class Graph
        {
        public:
            struct edge{int v,w,nx;} e[N<<1];
            int head[N]; int ecnt=-1;
            void clear() {MEM(e,-1); MEM(head,-1); ecnt=-1;}
            void add(int u,int v,int w)
            {
                e[++ecnt]=(edge){v,w,head[u]}; head[u]=ecnt;
            }
            void add2(int u,int v,int w)
            {
                add(u,v,w),add(v,u,w);
            }
            int& st(int u) {return head[u];}
            int& to(int i) {return e[i].v;}
            int& nx(int i) {return e[i].nx;}
            int& need(int i) {return e[i].w;}
        }G;
        int n,m;
        bool type[N]; // 低级:0; 高级:1
        int fa[N];
        int power[N],price[N],lim[N];
        void Input()
        {
            Rd(n,m); 
            G.clear(); MEM(fa,-1);
            F(i,1,n)
            {
                power[i]=I();
                char o[3]; scanf("%s",o);
                if (o[0]=='A')
                {
                    type[i]=1;
                    int tot=I();
                    F(j,1,tot)
                    {
                        int v,need; Rd(v,need);
                        G.add(i,v,need); fa[v]=i;
                    }
                }
                else
                {
                    type[i]=0;
                    Rd(price[i],lim[i]);
                }
            }
        }
        int f[N][N][M],g[N][M];
        void DFS(int u)
        {
            if (!type[u])
            {
                lim[u]=min(lim[u],m/price[u]);
                F(J,0,lim[u]) F(j,0,J)
                {
                    f[u][j][J*price[u]]=(J-j)*power[u];
                } 
                return;
            }
            lim[u]=1145141919;
            Tra(i,u)
            {
                DFS(v);
                price[u]+=price[v]*G.need(i);
                lim[u]=min(lim[u],lim[v]/G.need(i));
            }
            lim[u]=min(lim[u],m/price[u]);
            MEM(g,0xcf); g[0][0]=0;
            D(J,lim[u],0)
            {
                int t=0;
                Tra(i,u)
                {
                    ++t;
                    F(k,0,m) F(kk,0,k)
                    {
                        g[t][k]=max(g[t][k],g[t-1][kk]+f[v][J*G.need(i)][k-kk]);
                    }
                }
                F(j,0,J) F(k,0,m)
                {
                    f[u][j][k]=max(f[u][j][k],g[t][k]+(J-j)*power[u]);
                }
            }
        }
        int h[N][M];
        int mx[M];
        void Sakuya()
        {
            MEM(f,0xcf); MEM(g,0xcf); MEM(h,0xcf);
            h[0][0]=0;
            int t=0;
            F(i,1,n) if (fa[i]==-1)
            {
                DFS(i); ++t;
                F(k,0,m) 
                {
                    mx[k]=0;
                    F(j,0,lim[i]) mx[k]=max(mx[k],f[i][j][k]);
                }
                F(k,0,m) F(kk,0,k)
                {
                    h[t][k]=max(h[t][k],h[t-1][kk]+mx[k-kk]);
                }
            }
            int ans=0;
            F(i,0,m) ans=max(ans,h[t][i]);
            printf("%d
    ",ans);
        }
        void IsMyWife()
        {
            Input();
            Sakuya();
        }
    }
    #undef int //long long
    int main()
    {
        Flandre_Scarlet::IsMyWife();
        getchar();
        return 0;
    }
    
  • 相关阅读:
    js实现继承的5种方式
    JavaScript文件操作(1)-基础
    12 个免费在线的 Web 网站性能测试工具
    server r2 系统更新文件清理
    在C#中,Json的序列化和反序列化的几种方式总结
    ES6,新增数据结构Set的用法
    ES6中有关数组的一些新操作
    vue中mode hash 和 history的区别
    POST和GET的区别
    前端的细节
  • 原文地址:https://www.cnblogs.com/LightningUZ/p/14619687.html
Copyright © 2011-2022 走看看