JSOI 2008 魔兽地图

题目描述

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,表示最多可以提升多少点力量值。

输入输出样例

输入样例#1: 复制
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
输出样例#1: 复制
33


//By Monster_Qi
#include<bits/stdc++.h>

using namespace std;
const int MAXM = 2005;
const int MAXN = 55;
const int inf = 0x3f3f3f3f;

struct Edge{
    int nxt,to,val;
}edge[MAXN*MAXN];
                //dp[x][i][j]表示以x为节点,i个用于合成,花费j元的最大收益。 
int n,m,head[MAXN],cnt,dp[MAXN][MAXN*2][MAXM]; 
int L[MAXN],M[MAXN],war[MAXN];
   //L[x]表示以x为根的子树最多合成几个,M[x]表示以x为根的子树最大花费。 
int ans[MAXM],f[MAXM]; //ans[x]表示花费x元的最大收益,f[x]表示花费x元的局部最大收益。 
bool vis[MAXN],used[MAXN];

inline void add(int bg,int ed,int w){
    edge[++cnt].to=ed;
    edge[cnt].val=w;
    edge[cnt].nxt=head[bg];
    head[bg]=cnt;
}

inline void dfs(int x){
    if(vis[x]) return;
    vis[x]=1;
    if(!head[x]){      //如果为叶节点。
        L[x]=min(L[x],m/M[x]);    
        for(register int i=L[x];i>=0;i--)     //枚举几个用来合成。 
            for(register int j=i;j<=L[x];j++)   //枚举一共买了几个。 
                dp[x][i][M[x]*j]=war[x]*(j-i);
        return;
    }
    L[x]=inf;
    for(register int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].to;dfs(v);
        L[x]=min(L[x],L[v]/edge[i].val);
        M[x]+=edge[i].val*M[v];
    }
    L[x]=min(L[x],m/M[x]);    //排除可能因钱不够的影响。 
    for(register int i=L[x];i>=0;i--){     //枚举买几个此物品。 
        memset(f,-0x3f,sizeof(f));
        f[0]=0;
        for(register int u=head[x];u;u=edge[u].nxt){
            int v=edge[u].to;
            for(register int j=m;j>=0;j--){     //枚举一共的花费。 
                int sum=-inf;
                for(register int k=0;k<=j;k++){  //枚举以v为根的子树的花费。 
                    sum=max(sum,f[j-k]+dp[v][edge[u].val*i][k]);
                             //其他物品的收益加上以v为根的子树的最大花费。 
                    f[j]=sum;
                }
            }
        }
        for(register int j=0;j<=i;j++)  //枚举有几个用来合成。 
            for(register int k=0;k<=m;k++)   //枚举花费多少。 
                dp[x][j][k]=max(dp[x][j][k],f[k]+war[x]*(i-j));
    }
}

int main(){
    scanf("%d%d",&n,&m);
    memset(dp,-0x3f,sizeof(dp));
    for(register int i=1;i<=n;i++){
        scanf("%d",&war[i]);
        char c;
        cin>>c;
//      cout<<c<<endl;
        if(c=='B')
            scanf("%d%d",&M[i],&L[i]);
        else{
            int nn;
            scanf("%d",&nn);
            for(register int j=1;j<=nn;j++){
                int a,b;
                scanf("%d%d",&a,&b);
                add(i,a,b);
                used[a]=1;
            }
        }
    }
//  for(register int i=1;i<=n;i++)  cout<<used[i]<<endl;
    for(register int i=1;i<=n;i++)
        if(!used[i]){
            dfs(i);
            for(register int j=m;j>=0;j--)    //枚举总共花了多少钱。 
                for(register int k=0;k<=j;k++)    //枚举以i为根的子树的花费。 
                    ans[j]=max(ans[j],ans[j-k]+dp[i][0][k]);
        }
//  for(register int i=1;i<=m;i++) cout<<ans[i]<<endl;
    printf("%d\n",ans[m]);
    return 0;
}
posted @ 2018-06-05 10:39  Monster_Qi  阅读(115)  评论(0编辑  收藏  举报