JSOI2008魔兽地图
[JSOI2008]魔兽地图
题目描述
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(幽灵附体)作为回报的。
输入格式
第一行包含两个整数,N (1 <= n <= 51) 和 m (0 <= m <= 2,000)。分别表示装备的种类数和金币数。装备用1到N的整数编号。
接下来的N行,按照装备1到装备n的顺序,每行描述一种装备。
每一行的第一个非负整数表示这个装备贡献的力量值。
接下来的非空字符表示这种装备是基本装备还是高级装备,A表示高级装备,B表示基本装备。如果是基本装备,紧接着的两个正整数分别表示它的单价(单位为金币)和数量限制(不超过100)。如果是高级装备,后面紧跟着一个正整数C,表示这个高级装备需要C种低级装备。后面的2C个数,依次描述某个低级装备的种类和需要的个数。
输出格式
第一行包含一个整数S,表示最多可以提升多少点力量值。
思路
打过\(\text {dota}\)类游戏的同志看了题面一下子画面感就出来了,再看到有金币选择装备购买,立马就能想到是一道树形和背包的混合\(\text {dp}\)。
设计状态:一维肯定是用来表示所讨论的装备(树上的当前点),一维和普通背包一样表示用来买装备的钱;此题的特殊点在于,高级装备可以被下层装备合成,也就是说当前状态还会影响上层,接下来再设计一维讨论用以合成的装备数量,就有了如下的状态。
\(d_{i, j, l}\) \(i\) : 装备序号 ; \(j\) : 使用的钱 ; \(l\) : 用以合成高级装备的数量
设计转移:\(d_{x, j, l} = \sum_{fa[To] = x, 1 \le l \le j, 0\le k \le limit} d_{x, j-l, k \times edgelen} + d_{To, l, k \times edgelen}\)
上面的方程需要注意是不能直接套用的,因为决策中会影响到后面的值,在实际运用中要另开数组存下来,最后再赋值到\(\text {dp}\)数组中。
决策:对于每个点,有两种决策。
-
如果是基础装备,则直接讨论算出不同上交装备方案的值;
-
若是高级装备,则先算出题目没有给出,但实际上可由下层装备算出的\(limit[x]\)和\(cost[x]\)。
接下来进行 \(\text {dp}\) ,最后再将 \(\text g\) 数组的值赋回 \(\text {dp}\) 数组中。
最后因为可能是森林——有多个不联通的树,所以\(ans\)答案数组也要对多个树进行背包计算。
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 55, MAXM = 20005;
struct Tree{
int to, Next, v;
}a[MAXM];
int n, m, newp;
int first[MAXN], p[MAXN], lim[MAXN], c[MAXN], rd[MAXN], d[MAXN][2005][105], vis[MAXN], g[2005], ans[2005];
inline void add(int x, int y, int l){
a[++newp] = (Tree){y, first[x], l};
first[x] = newp;
}
void dfs(int x){
if(vis[x]) return ; vis[x] = 1;
if(!first[x]){
lim[x] = min(lim[x], m/c[x]);
for(int i = lim[x]; i >= 0; i--)
for(int j = i; j <= lim[x]; j++)
d[x][j*c[x]][i] = (j-i) * p[x];
return ;
}
for(int i = first[x]; i; i = a[i].Next){
int To = a[i].to;
dfs(To);
lim[x] = min(lim[x], lim[To]/a[i].v);
c[x] += c[To] * a[i].v;
}
lim[x] = min(lim[x], m/c[x]);
for(int k = lim[x]; k >= 0; k--){
memset(g, -0x3f, sizeof(g)); g[0] = 0;
for(int i = first[x]; i; i = a[i].Next){
int To = a[i].to;
for(int j = m; j >= 0; j--){
int t = -0x3f3f3f3f;
for(int l = 0; l <= j; l++){
t = max(t, g[j-l] + d[To][l][k*a[i].v]);
}
g[j] = t;
}
}
for(int i = 0; i <= k; i++)
for(int j = 0; j <= m; j++)
d[x][j][i] = max(d[x][j][i], g[j] + p[x]*(k-i));
}
}
inline int read(void){
int num = 0, f = 1;
char ch;
while(!isdigit(ch=getchar())) if(ch == '-') f = -1;
while(isdigit(ch)) num = num*10 + ch-'0', ch = getchar();
return num * f;
}
int main()
{
memset(d, -0x3f, sizeof(d));
memset(lim, 0x3f, sizeof(lim));
n = read(), m = read();
for(int i = 1; i <= n; i++){
p[i] = read();
char o; cin >> o;
if(o == 'A'){
int c = read();
while(c--){
int x = read(), y = read();
add(i, x, y); rd[x]++;
}
}
else {
c[i] = read(), lim[i] = read();
}
}
for(int i = 1; i <= n; i++){
if(!rd[i]){
dfs(i);
for(int j = m; j >= 0; j--)
for(int l = 0; l <= j; l++)
ans[j] = max(ans[j], ans[j-l] + d[i][l][0]);
}
}
printf("%d\n", ans[m]);
return 0;
}

浙公网安备 33010602011771号