【BZOJ 1017】 [JSOI2008]魔兽地图DotR

来源:互联网 发布:html5 css javascript 编辑:程序博客网 时间:2024/04/29 09:43

1017: [JSOI2008]魔兽地图DotR

Time Limit: 30 Sec  Memory Limit: 162 MB
Submit: 1069  Solved: 433
[Submit][Status]

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


比较麻烦的树形dp。


注意题目中说了:合成的过程一定是满足一棵树的形状的!


dp方程:

f[i][j][k]表示第i个装备花k的钱,贡献j个给上级的最大力量,g[tot][j]表示当前是i的第tot个儿子,一共花费j的钱的最大力

量。


在这两个方程之上,我们需要嵌套一层循环all,表示第i个装备要做多少个。


那么我们先求g数组(要满足能做all个i):

g[tot][j]=max(g[tot][j],g[tot-1][j-k]+f[y][all*num[tot]][k])  

(k是花多少的钱做当前的儿子,y是当前儿子的编号,num[tot]表示做1个i用几个tot)


在当前的all下继续求f数组:

f[i][j][k]=max(f[i][j][k],g[tot][k]+p[i]*(all-j))  


注意:

now要从大到小循环,保证g数组是单增的,就可以避免每次清空g数组了。


#include <iostream>#include <cstring>#include <algorithm>#include <cstdio>#include <cmath>#include <cstdlib>#define inf 0x3f3f3f3fusing namespace std;int in[60],h[60],tot=0,n,m,g[60][2005],l[60],p[60],c[60],f[60][106][2005];struct edge{int y,ne,num;}e[200005];void Addedge(int x,int y,int v){tot++;e[tot].y=y;e[tot].ne=h[x];h[x]=tot;e[tot].num=v;in[y]++;}void dp(int x){if (!h[x]){l[x]=min(l[x],m/c[x]);for (int i=0;i<=l[x];i++)for (int j=i;j<=l[x];j++)f[x][i][j*c[x]]=p[x]*(j-i);return;}l[x]=m;for (int i=h[x];i;i=e[i].ne){int y=e[i].y;dp(y);l[x]=min(l[x],l[y]/e[i].num);c[x]+=e[i].num*c[y];}l[x]=min(l[x],m/c[x]);for (int i=1;i<=n;i++)for (int j=0;j<=m;j++)g[i][j]=-inf;g[0][0]=0;for (int all=l[x];all>=0;all--){int tot=0;for (int i=h[x];i;i=e[i].ne){tot++;int y=e[i].y;for (int j=0;j<=m;j++)for (int k=0;k<=j;k++)g[tot][j]=max(g[tot][j],g[tot-1][j-k]+f[y][all*e[i].num][k]);}for (int i=0;i<=all;i++)for (int k=0;k<=m;k++)f[x][i][k]=max(f[x][i][k],g[tot][k]+p[x]*(all-i));}}int main(){        scanf("%d%d",&n,&m);for (int i=1;i<=n;i++)for (int j=0;j<=105;j++)for (int k=0;k<=m;k++)f[i][j][k]=-inf;for (int i=1;i<=n;i++){scanf("%d",&p[i]);char s[5];scanf("%s",s);if (s[0]=='B') scanf("%d%d",&c[i],&l[i]);else{int k;scanf("%d",&k);while(k--){int y,v;scanf("%d%d",&y,&v);Addedge(i,y,v);}}}int ans=0;for (int i=1;i<=n;i++)if (!in[i]){dp(i);for (int j=0;j<=m;j++)for (int k=0;k<=l[i];k++)ans=max(ans,f[i][k][j]);break;}cout<<ans<<endl;return 0;}




感悟:

1.这道题f[i][j][k]这种转移方式中j表示给上级几个,这种转移方式还是第一次见!


2.这道题vfk有二维解法,以后看看

2 0
原创粉丝点击