【树形Dp】【JSOI2008】【BZOJ1017魔兽地图DotR】
2013-04-30 21:07
537 查看
【说明】这是VFleaking的题解。参见:http://vfleaking.blog.163.com/blog/static/17480763420130242646240/。但是觉得他有些地方没说清楚吧。
【题解】这是他的代码。
【代码】
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)
= 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,也就是代码中的这个部分:
4~5行表示,能合成就合成,最终变成了1件装备u,7~8行表示,不需要能合成就合成,在2者中取最大值,得到f。
PS:关于relaxDp的过程,有段代码是:
它的意思是,花了更多钱却得到少的力量值,这种状态属于无效状态(VFleaking一句话就让我明白了这个地方的优化,确实很给力,不加这段2000+MS,加了这段300+MS)
【题解】这是他的代码。
【代码】
#include <iostream> #include <cstdio> #include <algorithm> #include <climits> using namespace std; const int MaxN = 51; const int MaxM = 2000; struct equipment { int energy; int nNeed; int needIndex[MaxN]; int needSum[MaxN]; int money; int maxsum; char type; friend inline istream& operator>>(istream &in, equipment &e) { in >> e.energy >> e.type; switch (e.type) { case 'A': in >> e.nNeed; for (int i = 0; i < e.nNeed; i++) in >> e.needIndex[i] >> e.needSum[i]; break; case 'B': e.nNeed = 0; in >> e.money >> e.maxsum; break; } return in; } }; equipment e[MaxN + 1]; int n, m; int f[MaxN + 1][MaxM + 1]; int g[MaxN + 1][MaxM + 1]; template <class T> inline bool tension(T &a, const T &b) { if (b < a) { a = b; return true; } return false; } template <class T> inline bool relax(T &a, const T &b) { if (b > a) { a = b; return true; } return false; } void getInformation(int idx) { equipment *cur = &e[idx]; if (cur->type == 'A') { cur->money = 0; cur->maxsum = INT_MAX; for (int i = 0; i < cur->nNeed; i++) { getInformation(cur->needIndex[i]); cur->money += e[cur->needIndex[i]].money * cur->needSum[i]; tension(cur->maxsum, e[cur->needIndex[i]].maxsum / cur->needSum[i]); } tension(cur->maxsum, m / cur->money); } } inline bool relaxDP(int *a, int *b) { bool updated = false; for (int i = m - 1; i >= 0; i--) if (a[i] > 0 || i == 0) for (int j = 1; i + j <= m; j++) if (b[j] > 0 && relax(a[i + j], a[i] + b[j])) updated = true; int maxV = 0; for (int i = 0; i <= m; i++) { if (a[i] <= maxV) a[i] = 0; else maxV = a[i]; } return updated; } void dfs(int idx, int sum) { equipment *cur = &e[idx]; for (int i = 0; i < e[idx].nNeed; i++) dfs(cur->needIndex[i], cur->maxsum * cur->needSum[i]); int *curF = f[idx], *curG = g[idx]; static int h[MaxM + 1]; fill(h, h + m + 1, 0); for (int i = 0 ; i < cur->nNeed; i++) { relaxDP(h, f[cur->needIndex[i]]); for (int j = 0; j < cur->needSum[i]; j++) if (!relaxDP(curG, g[cur->needIndex[i]])) break; } bool updated = true; for (int i = cur->maxsum - sum; i >= 0; i--) { int moneyi = cur->money * i, energyi = cur->energy * i; for (int j = moneyi; j <= m; j++) relax(curF[j], h[j - moneyi] + energyi); if (updated) updated = relaxDP(h, curG); } if (cur->money <= m) relax(curG[cur->money], cur->energy); } inline int handle(int root) { getInformation(root); dfs(root, 0); return *max_element(f[root], f[root] + m + 1); } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) cin >> e[i]; static bool isRoot[MaxN + 1]; fill(isRoot + 1, isRoot + n + 1, true); for (int i = 1; i <= n; i++) if (e[i].type == 'A') for (int j = 0; j < e[i].nNeed; j++) isRoot[e[i].needIndex[j]] = false; int root = find(isRoot + 1, isRoot + n + 1, true) - isRoot; cout << handle(root) << endl; return 0; }
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)
= 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,也就是代码中的这个部分:
for (int i = cur->maxsum - sum; i >= 0; i--) { int moneyi = cur->money * i, energyi = cur->energy * i; for (int j = moneyi; j <= m; j++) relax(curF[j], h[j - moneyi] + energyi); if (updated) updated = relaxDP(h, curG); }
4~5行表示,能合成就合成,最终变成了1件装备u,7~8行表示,不需要能合成就合成,在2者中取最大值,得到f。
PS:关于relaxDp的过程,有段代码是:
int maxV = 0; for (int i = 0; i <= m; i++) { if (a[i] <= maxV) a[i] = 0; else maxV = a[i]; }
它的意思是,花了更多钱却得到少的力量值,这种状态属于无效状态(VFleaking一句话就让我明白了这个地方的优化,确实很给力,不加这段2000+MS,加了这段300+MS)
相关文章推荐
- [BZOJ1017][JSOI2008]魔兽地图DotR 树形dp
- bzoj1017 [JSOI2008]魔兽地图DotR 树形DP
- BZOJ.1017.[JSOI2008]魔兽地图(树形DP 背包DP)
- BZOJ1017 [JSOI2008]魔兽地图DotR 【树形dp + 背包dp】
- [bzoj1017][JSOI2008]魔兽地图 DotR (Tree DP)【有待优化】
- bzoj 1017: [JSOI2008]魔兽地图DotR【树形dp+背包】
- bzoj 1017: [JSOI2008]魔兽地图DotR (树形DP+多重背包)
- [bzoj1017][JSOI2008]魔兽地图DotR【dp】
- [BZOJ1017][JSOI2008][树形DP]魔兽地图DotR
- 【BZOJ1017】[JSOI2008]魔兽地图DotR
- 【East!模拟赛】【Round1】【BZOJ1017】魔兽地图Dotr 树形DP
- 【树形背包】【JSOI 2008】【bzoj 1017】魔兽地图DotR
- 1017: [JSOI2008]魔兽地图DotR - BZOJ
- [bzoj1017]:[JSOI2008]魔兽地图DotR
- [JSOI2008][BZOJ1017] 魔兽地图DotR|树型dp
- bzoj1017 [JSOI2008]魔兽地图DotR(树形dp+背包dp+剪枝)
- 【BZOJ】1040 [ZJOI2008]骑士 树形DP
- BZOJ 1040 ZJOI 2008 骑士 基环树林+树形DP
- BZOJ 4753 [Jsoi2016]最佳团体 ——01分数规划 树形DP
- BZOJ 1040 ZJOI2008 骑士 树形DP