【来源】
【题目描述】
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,表示最多可以提升多少点力量值。
【样例输出】
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
【样例输出】
33
【解析】
树形dp。
f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示以i为根的子树刚好花了k元,把j个贡献给父亲,此时的最大值,可以先计算全部贡献给父亲,然后计算部分贡献给父亲,注意用lim数组限制每次的枚举量,即某个点最多可以有几个。
另外,这题还有一个特殊情况,就是所有的点都没有父亲的情况,此时要做一个多重背包.
【代码】
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define RI register int
#define re(i,a,b) for(RI i=a; i<=b; i++)
#define ms(i,a) memset(a,i,sizeof(a))
#define MAX(a,b) (((a)>(b)) ? (a):(b))
#define MIN(a,b) (((a)<(b)) ? (a):(b))
using namespace std;
typedef long long LL;
namespace IO {
#include <cctype>
template <typename T>
inline void read(T &x){
x=0;
char c=0;
T w=0;
while (!isdigit(c)) w|=c=='-',c=getchar();
while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(w) x=-x;
}
template <typename T>
inline void write(T x) {
if(x<0) putchar('-'),x=-x;
if(x<10) putchar(x+'0');
else write(x/10),putchar(x%10+'0');
}
template <typename T>
inline void writesp(T x) {
write(x);
putchar(' ');
}
template <typename T>
inline void writeln(T x) {
write(x);
putchar('\n');
}
}
using IO::read;
using IO::write;
using IO::writesp;
using IO::writeln;
const int N=55;
const int M=2005;
const int inf=1e9;
struct Edge {
int to,nt;
} e[M<<3];
int n,m,ans,cnt;
int v[N],lim[N],g[M],need[N],cost[N],h[N],fa[N];
int dp[N][M];
int f[N][105][M];
inline void add(int a,int b) {
e[++cnt]=(Edge){
b,h[a]};
h[a]=cnt;
}
void dfs(int k) {
if(!h[k]) {
lim[k]=MIN(lim[k],m/cost[k]);
for(int i=0; i<=lim[k]; i++) for(int j=0; j<=i; j++)
f[k][j][i*cost[k]]=(i-j)*v[k];
return;
}
for(int i=h[k]; i; i=e[i].nt) {
dfs(e[i].to);
lim[k]=MIN(lim[k],lim[e[i].to]/need[e[i].to]);
}
for(int i=0; i<=lim[k]; i++) f[k][i][0]=0;
for(int i=h[k]; i; i=e[i].nt) {
for(int j=0; j<=lim[k]; j++) {
memcpy(g,f[k][j],sizeof(f[k][j]));
memset(f[k][j],-1,sizeof(f[k][j]));
for(int x=m; x>=0; x--) for(int r=x; r>=0; r--)
if(g[x-r]!=-1 && f[e[i].to][j*need[e[i].to]][r]!=-1) {
f[k][j][x]=MAX(f[k][j][x],g[x-r]+f[e[i].to][j*need[e[i].to]][r]);
ans=MAX(f[k][j][x],ans);
}
}
}
for(int i=0; i<=lim[k]; i++) for(int j=i; j<=lim[k]; j++)
for(int x=0; x<=m; x++) if(f[k][j][x]!=-1) {
f[k][i][x]=MAX(f[k][i][x],f[k][j][x]+(j-i)*v[k]);
ans=MAX(ans,f[k][i][x]);
}
}
int main() {
read(n),read(m);
for(int i=1; i<=n; i++) lim[i]=inf;
for(int i=1; i<=n; i++) {
read(v[i]);
char s[2];
scanf("%s",s);
if(s[0]=='B') read(cost[i]),read(lim[i]);
else {
int k;
read(k);
while(k--) {
int x,y;
read(x),read(y);
fa[x]=1;
add(i,x);
need[x]=y;
}
}
}
int check=0;
memset(f,-1,sizeof(f));
for(int i=1; i<=n; i++)
if(!fa[i]) dfs(i);
else check=1;
if(!check) {
for(int i=1; i<=n; i++) for(int j=0; j<=m; j++)
for(int k=0; k<=100; k++) {
if(cost[i]*k>j || k>lim[i]) continue;
dp[i][j]=MAX(dp[i][j],dp[i-1][j-cost[i]*k]+v[i]*k);
}
ans=dp[n][m];
}
writeln(ans);
return 0;
}