@(BZOJ)[樹形DP, 三維DP]

Description

DotR (Defense of the Robots) Allstars是一个风靡全球的魔兽地图,他的规则简单与同样流行的地图DotA

(Defense of the Ancients) Allstars。DotR里面的英雄只有一个属性——力量。他们需要购买装备来提升自己的

力量值,每件装备都可以使佩戴它的英雄的力量值提高固定的点数,所以英雄的力量值等于它购买的所有装备的力

量值之和。装备分为基本装备和高级装备两种。基本装备可以直接从商店里面用金币购买,而高级装备需要用基本

装备或者较低级的高级装备来合成,合成不需要附加的金币。装备的合成路线可以用一棵树来表示。比如,Sange

and Yasha的合成需要Sange,Yasha和Sange and Yasha Recipe Scroll三样物品。其中Sange又要用Ogre Axe, Belt

of Giant Strength和 Sange Recipe Scroll合成。每件基本装备都有数量限制,这限制了你不能无限制地合成某

些性价比很高的装备。现在,英雄Spectre有M个金币,他想用这些钱购买装备使自己的力量值尽量高。你能帮帮他

吗?他会教你魔法Haunt(幽灵附体)作为回报的。

Input

第一行包含两个整数,N (1 <= n <= 51) 和 m (0 <= m <= 2,000)。分别表示装备的种类数和金币数。装备

用1到N的整数编号。接下来的N行,按照装备1到装备n的顺序,每行描述一种装备。每一行的第一个非负整数表示这

个装备贡献的力量值。接下来的非空字符表示这种装备是基本装备还是高级装备,A表示高级装备,B表示基本装备

。如果是基本装备,紧接着的两个正整数分别表示它的单价(单位为金币)和数量限制(不超过100)。如果是高

级装备,后面紧跟着一个正整数C,表示这个高级装备需要C种低级装备。后面的2C个数,依次描述某个低级装备的

种类和需要的个数。

Output

第一行包含一个整数S,表示最多可以提升多少点力量值。

Sample Input

10 59
5 A 3 6 1 9 2 10 1
1 B 5 3
1 B 4 3
1 B 2 3
8 A 3 2 1 3 1 7 1
1 B 5 3
5 B 3 3
15 A 3 1 1 5 1 4 1
1 B 3 5
1 B 4 3

Sample Output

33

Solution

樹形DP.

數組\(f[i][j][k]\)用於記錄當前以\(i\)為根的子樹中, 留下\(j\)個\(i\)裝備給父節點, 并在子樹中總共使用\(k\)個金幣, 所能得到的以\(i\)為根的這棵子樹中可以得到的最大能力值.

嗯, 聽起來很晦澀, 實際上也很晦澀..

然後這題有個莫名的坑點, 這棵裝備樹有可能只有葉子節點, 而沒有任何非葉子節點..所以要加一個if語句進行特判, 假如只有基本裝備的話, 則直接背包DP搞定它.

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring>
using namespace std;
inline void read(int &x)
{
x = 0;
int flag = 1;
char c;
while(! isdigit(c = getchar()))
if(c == '-')
flag *= - 1;
while(isdigit(c))
x = x * 10 + c - '0', c = getchar();
x *= flag;
}
void read(char &c)
{
while(! isgraph(c = getchar()));
}
void println(int x)
{
if(x < 0)
putchar('-'), x *= - 1;
if(x == 0)
putchar('0');
int ans[10 + (1 << 4)], top = 0;
while(x)
ans[top ++] = x % 10, x /= 10;
for(; top; top --)
putchar(ans[top - 1] + '0');
putchar('\n');
}
const int N = 1 << 6;
const int M = 1 << 11;
const int LIM = 1 << 7;
int n, m, ans;
struct Equipment
{
int val, cost, lim, pre, need;
}a[N];
struct Edge
{
int v, next;
}G[N];
int top;
int head[N];
void add_edge(int u, int v)
{
G[top].v = v, G[top].next = head[u];
head[u] = top ++;
a[v].pre = u;
}
int f[N][LIM][M], tmp[M];
//f[i][j][k]記錄以i為根的子樹中使用k的金額並且留下j個i給父親節點時, 這棵子樹可以達到的最大能力值
void solve(int u)
{
if(! (~ head[u]))
{
a[u].lim = min(a[u].lim, m / a[u].cost);
for(int i = 0; i <= a[u].lim; i ++) //遍歷當前點留給父親節點的數量
for(int j = i; j <= a[u].lim; j ++) //遍歷當前點總共取的數量
f[u][i][j * a[u].cost] = (j - i) * a[u].val;
return;
}
a[u].lim = M;
for(int i = head[u]; ~ i; i = G[i].next)
solve(G[i].v), a[u].lim = min(a[u].lim, a[G[i].v].lim / a[G[i].v].need);
for(int i = 0; i <= a[u].lim; i ++)
f[u][i][0] = 0;
for(int i = head[u]; ~ i; i = G[i].next) //遍歷每一個子節點
{
int v = G[i].v;
for(int j = 0; j <= a[u].lim; j ++) //遍歷每一個當前點留給父親節墊的數量
{
for(int k = 0; k <= m; k ++)
tmp[k] = f[u][j][k];
memset(f[u][j], - 1, sizeof(f[u][j]));
//複製信息以用於更新
for(int k = 0; k <= m; k ++) //遍歷當前子樹中總共使用的價值
for(int l = 0; l <= k; l ++) //遍歷以子節點v為根的子樹中總共使用的價值
if(~ tmp[k - l] && ~ f[v][j * a[v].need][l])
f[u][j][k] = max(f[u][j][k], f[v][j * a[v].need][l] + tmp[k - l]);
}
}
/*
到這裡貌似就已經寫完了
但實際上還忽略了一種情況
就是當前購買的u節點並不全部留給父節點, 而是有一些(或者全部)留著直接加入進能力值中
因此就會有下面的一段代碼
*/
for(int i = 0; i <= a[u].lim; i ++)
for(int j = i; j <= a[u].lim; j ++)
for(int k = 0; k <= m; k ++)
if(~ f[u][j][k])
f[u][i][k] = max(f[u][i][k], f[u][j][k] + (j - i) * a[u].val), ans = max(ans, f[u][i][k]);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ1017.in", "r", stdin);
freopen("BZOJ1017.out", "w", stdout);
#endif
read(n), read(m);
for(int i = 1; i <= n; i ++)
a[i].pre = head[i] = - 1;
top = 0; //WOC就是這裡一開始寫成了- 1, 結果熬夜調試了一整個晚上QAQ
for(int i = 1; i <= n; i ++)
{
read(a[i].val);
char opt;
read(opt);
if(opt == 'B')
read(a[i].cost), read(a[i].lim);
else
{
int j;
read(j);
while(j --)
{
int x;
read(x);
add_edge(i, x);
read(a[x].need);
}
}
}
int flag = 1;
for(int i = 1; i <= n; i ++)
if(~ a[i].pre)
flag = 0;
if(flag)
{
int f[N][M];
f[0][0] = 0;
for(int i = 1; i <= n; i ++)
for(int j = 0; j <= a[i].lim; j ++)
for(int k = m; k >= j * a[i].cost; k --)
f[i][k] = max(f[i][k], f[i - 1][k - j * a[i].cost] + j * a[i].val);
for(int i = 0; i <= m; i ++)
ans = max(ans, f[n][i]);
println(ans);
return 0;
}
ans = - 1;
memset(f, - 1, sizeof(f));
for(int i = 1; i <= n; i ++)
if(! (~ a[i].pre))
solve(i), flag = 1;
println(ans);
}

BZOJ1017魔兽地图DotR 樹形DP的更多相关文章

  1. BZOJ1017: [JSOI2008]魔兽地图DotR【树形DP】【玄学】

    Description DotR (Defense of the Robots) Allstars是一个风靡全球的魔兽地图,他的规则简单与同样流行的地图DotA (Defense of the Anc ...

  2. BZOJ1017 [JSOI2008]魔兽地图DotR 【树形dp + 背包dp】

    题目链接 BZOJ1017 题解 orz hzwer 树形dp神题 设\(f[i][j][k]\)表示\(i\)号物品恰好花费\(k\)金币,并将\(j\)个物品贡献给父亲的合成时的最大收益 计算\( ...

  3. BZOJ 1017 魔兽地图DotR(树形DP)

    题意:有两类装备,高级装备A和基础装备B.现在有m的钱.每种B有一个单价和可以购买的数量上限.每个Ai可以由Ci种其他物品合成,给出Ci种其他物品每种需要的数量.每个装备有一个贡献值.求最大的贡献值. ...

  4. BZOJ1017 魔兽地图DotR (树上背包)

    一道背包的神题,用到了树上dp和背包dp,这个题的特殊性在于儿子对于父亲节点是有影响的,所以用f[i][j][k]表示第i号装备,其中用j个来合成上层装备,花费k元所能获得最大的力量值. 然后对于每一 ...

  5. bzoj 1017: [JSOI2008]魔兽地图DotR【树形dp+背包】

    bzoj上是一个森林啊--? dp还是太弱了 设f[i][j][k]为到点i,合成j个i并且花费k金币能获得的最大力量值,a[i]为数量上限,b[i]为价格,p[i]为装备力量值 其实这个状态设计出来 ...

  6. 【BZOJ-1017】魔兽地图DotR 树形DP + 背包

    1017: [JSOI2008]魔兽地图DotR Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 1566  Solved: 705[Submit][S ...

  7. [BZOJ1017][JSOI2008]魔兽地图DotR 树形dp

    1017: [JSOI2008]魔兽地图DotR Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 2597  Solved: 1010[Submit][ ...

  8. 【bzoj1017】[JSOI2008]魔兽地图DotR

    1017: [JSOI2008]魔兽地图DotR Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 1658  Solved: 755[Submit][S ...

  9. BZOJ [JSOI2008]魔兽地图DotR

    1017: [JSOI2008]魔兽地图DotR Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 1243  Solved: 532[Submit][S ...

随机推荐

  1. [LUOGU] P3128 [USACO15DEC]最大流Max Flow

    题意:一棵树,多次给指定链上的节点加1,问最大节点权值 n个点,n-1条边很容易惯性想成一条链,幸好有样例.. 简单的树剖即可!(划去) 正常思路是树上差分,毕竟它就询问一次.. #include&l ...

  2. 【Java_多线程并发编程】基础篇—Thread类中start()和run()方法的区别

    1. start() 和 run()的区别说明 start()方法: 它会启动一个新线程,并将其添加到线程池中,待其获得CPU资源时会执行run()方法,start()不能被重复调用. run()方法 ...

  3. (20)zabbix触发器triggers

    触发器是什么 触发器(triggers)是什么?触发器使用逻辑表达式来评估通过item获取到得数据是处于哪种状态,item一收回数据,讲解任务交给触发器去评估状态,明白触发器是怎么一回事了把?在触发器 ...

  4. Linux服务器的弱口令检测及端口扫描

    一.弱口令检测--John the Ripper John the Ripper工具可以帮助我们扫描出系统中密码安全性较低的用户,并将扫描后的结果显示出来. 1.安装John the Ripper: ...

  5. leetcode-3-basic-divide and conquer

    解题思路: 因为这个矩阵是有序的,所以从右上角开始查找.这样的话,如果target比matrix[row][col]小,那么就向左查找:如果比它大,就 向下查找.如果相等就找到了,如果碰到边界,就说明 ...

  6. 数据结构之--图(Graphics)

    1.1:图的定义和术语   图是一种比线性表和树更为复杂的数据结构.在线性表中,数据元素之间仅有线性关系,每个元素仅有一个直接前驱和一个直接后继:在树形结构中,数据元素之间有着明显的层次关系,并且每一 ...

  7. Android自动化测试Uiautomator--UiCollection接口简介

    这个对象可以理解为一个对象的集合,因为UiSelector描述后得到的有可能是多个满足条件的控件集合,因此可以用来生成UiCollection,继承自UiObject. 用于枚举一个容器的用户界面(U ...

  8. python基础学习笔记——列表技巧

    列表: 循环删除列表中的每⼀个元素 li = [, , , ] for e in li: li.remove(e) print(li) 结果: [, ] 分析原因: for的运⾏过程. 会有⼀个指针来 ...

  9. Java设计模式学习二

    Java设计思想之单例模式 单例模式(Singleton Pattern)是Java中最常见的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式涉及到一个单一的 ...

  10. 对Thymeleaf的一些笼统介绍和理解

    (随手记录的,,可能没那么易看,sorry le) 先大概介绍一下关于Thymeleaf的概念和理解:首先Thymeleaf模板引擎(换句话说他是现代服务器端的Java模板引擎,) 他所对应的主要作用 ...