标签:bzoj oi dp
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-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有二维解法,以后看看
【BZOJ 1017】 [JSOI2008]魔兽地图DotR
标签:bzoj oi dp
原文地址:http://blog.csdn.net/regina8023/article/details/43014553