1017: [JSOI2008]魔兽地图DotR
Time Limit: 30 Sec Memory Limit: 162 MB
Submit: 1069 Solved: 433
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-1][j],g[tot-1][j-k]+f[y][all*num[tot]][k])
(k是花多少的钱做当前的儿子,y是当前儿子的编号,num[tot]表示做1个i用几个tot)
在当前的now下继续求f数组:
f[i][j][k]=max(f[i][j][k],g[tot][k]+p[i]*(all-j))
注意:
now要从大到小循环,保证g数组是单增的,就可以避免每次清空g数组了。
<span style="font-size:18px;">#include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <cmath> #include <cstdlib> #define inf 0x3f3f3f3f using 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; } </span>
感悟:
1.这道题f[i][j][k]这种转移方式中j表示给上级几个,这种转移方式还是第一次见!
2.这道题vfk有二维解法,以后看看