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}\)数组中。

决策:对于每个点,有两种决策。

  1. 如果是基础装备,则直接讨论算出不同上交装备方案的值;

  2. 若是高级装备,则先算出题目没有给出,但实际上可由下层装备算出的\(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;
}
posted @ 2020-07-24 15:03  Michmh  阅读(178)  评论(0)    收藏  举报