【树形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)

 

 

posted on 2013-04-30 21:07  孤星ぁ紫辰  阅读(1056)  评论(0编辑  收藏  举报

导航