zoukankan      html  css  js  c++  java
  • 【树形Dp】【JSOI2008】【BZOJ1017魔兽地图DotR】

    【说明】这是VFleaking的题解。参见:http://vfleaking.blog.163.com/blog/static/17480763420130242646240/。但是觉得他有些地方没说清楚吧。

     

    【题解】这是他的代码。

    【代码】

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <algorithm>
      4 #include <climits>
      5 using namespace std;
      6 
      7 const int MaxN = 51;
      8 const int MaxM = 2000;
      9 
     10 struct equipment
     11 {
     12    int energy;
     13 
     14    int nNeed;
     15    int needIndex[MaxN];
     16    int needSum[MaxN];
     17 
     18    int money;
     19    int maxsum;
     20 
     21    char type;
     22 
     23    friend inline istream& operator>>(istream &in, equipment &e)
     24    {
     25        in >> e.energy >> e.type;
     26 
     27        switch (e.type)
     28        {
     29            case 'A':
     30                in >> e.nNeed;
     31                for (int i = 0; i < e.nNeed; i++)
     32                    in >> e.needIndex[i] >> e.needSum[i];
     33                break;
     34 
     35            case 'B':
     36                e.nNeed = 0;
     37                in >> e.money >> e.maxsum;
     38                break;
     39        }
     40 
     41        return in;
     42    }
     43 };
     44 
     45 equipment e[MaxN + 1];
     46 int n, m;
     47 
     48 int f[MaxN + 1][MaxM + 1];
     49 int g[MaxN + 1][MaxM + 1];
     50 
     51 template <class T>
     52 inline bool tension(T &a, const T &b)
     53 {
     54    if (b < a)
     55    {
     56        a = b;
     57        return true;
     58    }
     59    return false;
     60 }
     61 template <class T>
     62 inline bool relax(T &a, const T &b)
     63 {
     64    if (b > a)
     65    {
     66        a = b;
     67        return true;
     68    }
     69    return false;
     70 }
     71 
     72 void getInformation(int idx)
     73 {
     74    equipment *cur = &e[idx];
     75    if (cur->type == 'A')
     76    {
     77        cur->money = 0;
     78        cur->maxsum = INT_MAX;
     79        for (int i = 0; i < cur->nNeed; i++)
     80        {
     81            getInformation(cur->needIndex[i]);
     82 
     83            cur->money += e[cur->needIndex[i]].money * cur->needSum[i];
     84            tension(cur->maxsum, e[cur->needIndex[i]].maxsum / cur->needSum[i]);
     85        }
     86        tension(cur->maxsum, m / cur->money);
     87    }
     88 }
     89 
     90 inline bool relaxDP(int *a, int *b)
     91 {
     92    bool updated = false;
     93 
     94    for (int i = m - 1; i >= 0; i--)
     95        if (a[i] > 0 || i == 0)
     96            for (int j = 1; i + j <= m; j++)
     97                if (b[j] > 0 && relax(a[i + j], a[i] + b[j]))
     98                    updated = true;
     99 
    100    int maxV = 0;
    101    for (int i = 0; i <= m; i++)
    102    {
    103        if (a[i] <= maxV)
    104            a[i] = 0;
    105        else
    106            maxV = a[i];
    107    }
    108 
    109    return updated;
    110 }
    111 
    112 void dfs(int idx, int sum)
    113 {
    114    equipment *cur = &e[idx];
    115    for (int i = 0; i < e[idx].nNeed; i++)
    116        dfs(cur->needIndex[i], cur->maxsum * cur->needSum[i]);
    117 
    118    int *curF = f[idx], *curG = g[idx];
    119    static int h[MaxM + 1];
    120    fill(h, h + m + 1, 0);
    121    for (int i = 0 ; i < cur->nNeed; i++)
    122    {
    123        relaxDP(h, f[cur->needIndex[i]]);
    124 
    125        for (int j = 0; j < cur->needSum[i]; j++)
    126            if (!relaxDP(curG, g[cur->needIndex[i]]))
    127                break;
    128    }
    129 
    130    bool updated = true;
    131    for (int i = cur->maxsum - sum; i >= 0; i--)
    132    {
    133        int moneyi = cur->money * i, energyi = cur->energy * i;
    134        for (int j = moneyi; j <= m; j++)
    135            relax(curF[j], h[j - moneyi] + energyi);
    136 
    137        if (updated)
    138            updated = relaxDP(h, curG);
    139    }
    140 
    141    if (cur->money <= m)
    142        relax(curG[cur->money], cur->energy);
    143 }
    144 
    145 inline int handle(int root)
    146 {
    147    getInformation(root);
    148    dfs(root, 0);
    149    return *max_element(f[root], f[root] + m + 1);
    150 }
    151 
    152 int main()
    153 {
    154    cin >> n >> m;
    155 
    156    for (int i = 1; i <= n; i++)
    157        cin >> e[i];
    158 
    159    static bool isRoot[MaxN + 1];
    160    fill(isRoot + 1, isRoot + n + 1, true);
    161    for (int i = 1; i <= n; i++)
    162        if (e[i].type == 'A')
    163            for (int j = 0; j < e[i].nNeed; j++)
    164                isRoot[e[i].needIndex[j]] = false;
    165 
    166    int root = find(isRoot + 1, isRoot + n + 1, true) - isRoot;
    167    cout << handle(root) << endl;
    168 
    169    return 0;
    170 }

    money[u] 表示装备u的单价

    maxsum[u]表示可购买装备u的最大数量

    needsum[u]表示合成装备u的父亲v,需要装备u的数量

    f[u][j]表示以u为根的子树上,正好花j元钱,所能取得的最大力量值,其中对于u本身来说,要满足一个条件限制:装备u的个数不能超过maxsum[u]-maxsum[v]*needsum[u] (v是u的父亲)

    对于树根root来说,它没有父亲,因此max{f[root][0] ~ f[root][m]}对应了最终的解。

    g[u][j]表示以u为根的子树上,正好花j元钱,所能取得的最大力量值,要满足的条件是:把这棵树上的所有装备全部换算成基本装备,要满足,装备t的个数不超过对于从u到t这个路线上的装备的needsum的乘积。  比如从根到叶分别是u-w-p-t,那么t的个数不超过needsum[w]*needsum[p]*needsum[t]。依次类推。

    PS:并不是一定要合成高级装备就能达到更大的力量值,因此g的意义在于,求出到底是以什么样的形态(哪些要合并成高级装备,哪些就保留不进行合成)来表现这些基本装备才能产生最大的力量值。

    relaxDp(f,g)[n] = max{f[i] + g[n-i]}   (0<=i<=n),f和g是2个一元函数,这个过程是枚举i,将总值n分成i和n-i两部分,分配给f,g,代表了以合理的分配方式分给2个函数之后,对于总值n能产生的最大效果。

    它满足交换律和结合律:

    relaxDp(f,g) = relaxDp(g,f)

    relaxDp(f,relaxDp(g,h)) = relaxDp(relax(f, g), h)

    下面为了方便,多个函数进行relaxDp时候,直接写成relaxDp(f1,f2,f3,f4,f5,...),某个函数进行k次relaxDp时候,写成relaxDp(f^k)

    对于一件装备u来说,它的父亲v最多需要maxsum[v]*needsum[u]个u,剩下的部分是与父亲绝对无关的部分,而f就代表了那剩下部分的最大值。求解f的过程如下:

    设u的儿子为u1,u2,u3...,首先令g[u][] = relaxDp(g[u1][] ^ needsum[u1] ,g[u2][] ^ needsum[u2], g[u3][] ^ needsum[u3],...),那么g[u][]就代表了以u为根的子树,正好花j元钱,能产生的最大值,其中满足条件是:把这棵树上的所有装备全部换算成基本装备,要满足,装备t的个数不超过对于从u到t这个路线上的装备的needsum的乘积(同上g的定义)。但是有1种情况需要排除,就是在钱够的基础下,能合成就合成,最终变成1个u,这种情况需要排除(这是显然的,因为根节点是需要我们去枚举的)。

    枚举究竟最终合成了几个装备u,也就是代码中的这个部分:

    1 for (int i = cur->maxsum - sum; i >= 0; i--)
    2    {
    3        int moneyi = cur->money * i, energyi = cur->energy * i;
    4        for (int j = moneyi; j <= m; j++)
    5            relax(curF[j], h[j - moneyi] + energyi);
    6 
    7        if (updated)
    8            updated = relaxDP(h, curG);
    9    }

    4~5行表示,能合成就合成,最终变成了1件装备u,7~8行表示,不需要能合成就合成,在2者中取最大值,得到f。

    PS:关于relaxDp的过程,有段代码是:

    1 int maxV = 0;
    2    for (int i = 0; i <= m; i++)
    3    {
    4        if (a[i] <= maxV)
    5            a[i] = 0;
    6        else
    7            maxV = a[i];
    8    }

    它的意思是,花了更多钱却得到少的力量值,这种状态属于无效状态(VFleaking一句话就让我明白了这个地方的优化,确实很给力,不加这段2000+MS,加了这段300+MS)

  • 相关阅读:
    Visual Studio2017 无法添加引用的解决方法
    第13周学习进度
    mininet之miniedit可视化操作
    构建之法阅读笔记05
    软件工程课堂练习找水王续
    第12周学习进度
    VS2015做单元测试
    学习调用第三方的WebService服务
    软件工程课堂练习找水王
    第11周学习进度
  • 原文地址:https://www.cnblogs.com/GXZC/p/3052412.html
Copyright © 2011-2022 走看看