JSOI 2008 【魔兽地图】

其实这题是我从noip前就开始做的。。。那个时候打的Pascal,一直TLE,转了C++之后我又写了一遍,A了。。。

辛酸史:

                      

题目描述:

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)<=51,金币数(m)<=2000,限购数量(limit)<=100。

思路分析:

可以说是一道树上背包DP吧。

观察题目,注意到:限购数量是题目的关键,对于一个物品,它合成几件,或是把几件用于父亲装备的合成,是题目恶心的地方,既然这样的话我们就把它设计到状态里面。然后再看看数据范围,限购小于等于100——很明显了,出题人就是想让我们把限购数量放进状态里面。再和普通的背包结合一下,得到状态,f[u][i][j]表示:对于以u为根的子树,在以u为根的子树上,我们投入i个金币,并且把j个物品u用于父亲装备的合成,以u为根的子树能产生的最大力量值

然后我们还需要一个g数组来辅助f数组的转移。g[i][j]表示:对于u的前i个儿子装备,在以它们为根的子树中投入j个金币,能得到的最大力量值。

转移时我们需要枚举的很重要的一环就是:制作几个u装备,从而让整个转移能够顺利地进行下去,我们把它设为l。

得到g数组的转移:g[i][j]=max(g[i][j],g[i-1][j-k]+f[v][k][l*need])       其中v为u的第i个儿子,而need则为制作一个装备u需要几个装备v,k为在以v为根的子树中花费k个金币。

通过g数组的转移,继而得到f数组的转移:f[u][i][j]=max(f[u][i][j],g[siz[u]][i]+w[u]*(l-j))       其中siz[u]表示u有几个儿子。

最后求答案的时候我们又需要一个dp数组来求解。(嗯,没错,一道题目,三个背包dp,我也不得不佩服出题人了——你是真TM的毒瘤!

最后这个就是最简单的背包dp了(如果这个都想不到的话,建议你前往xx省xx市实验学校,和某张姓老师学习一段时间)

状态dp[i][j],表示前i个“终极装备”(终极装备就是无法用于合成的装备),花费j个金币,能得到的最大力量值。转移的时候枚举一下在第i个装备上花费多少金币就可以了,具体不懂的话还是看代码吧,太简单了,也不想多讲了。

代码实现:

#include<bits/stdc++.h>
using namespace std;
const int inf=1000000000;
int f[55][2005][105],g[55][2005],dp[55][2005];
int a[55][55],need[55][55],limit[55],cost[55],w[55],siz[55];
bool root[55],son[55];
int n,m;
void dfs(int u){
    if (!son[u]){
        limit[u]=min(limit[u],m/cost[u]);
        for (int i=0;i<=limit[u];i++)
            for (int j=0;j<=i;j++)
                f[u][i*cost[u]][j]=w[u]*(i-j);
        return;
    }
    limit[u]=inf;
    for (int i=1;i<=siz[u];i++){
        dfs(a[u][i]);
        cost[u]+=cost[a[u][i]]*need[u][i];
        limit[u]=min(limit[u],limit[a[u][i]]/need[u][i]);
    }
    limit[u]=min(limit[u],m/cost[u]);
    memset(g,-0x3f,sizeof(g));
    g[0][0]=0;
    for (int l=limit[u];l>=0;l--){                          //这里倒着枚举l,是为了避免多次给g数组赋初值
        for (int i=1;i<=siz[u];i++)
            for (int j=0;j<=m;j++)
                for (int k=0;k<=j;k++)
                    g[i][j]=max(g[i][j],g[i-1][j-k]+f[a[u][i]][k][l*need[u][i]]);
        for (int i=0;i<=l;i++)
            for (int j=0;j<=m;j++)
                f[u][j][i]=max(f[u][j][i],g[siz[u]][j]+w[u]*(l-i));
    }
}
int main(){
    memset(f,-0x3f,sizeof(f));
    scanf("%d%d",&n,&m);
    int nn,ans=0;
    for (int i=1;i<=n;i++){
        scanf("%d",&w[i]);
        char ch[3]; scanf("%s",&ch);
        if (ch[0]=='A'){
            scanf("%d",&nn);
            for (int j=1;j<=nn;j++) scanf("%d%d",&a[i][j],&need[i][j]),root[a[i][j]]=true;
            son[i]=true; siz[i]=nn;
        } else scanf("%d%d",&cost[i],&limit[i]);
    }
    int now=0;
    for (int i=1;i<=n;i++)
        if (!root[i]){
            dfs(i); now++;
            for (int j=0;j<=m;j++)
                for (int k=0;k<=j;k++)
                    dp[now][j]=max(dp[now][j],dp[now-1][j-k]+f[i][k][0]);
        }
    for (int i=0;i<=m;i++) ans=max(ans,dp[now][i]);
    printf("%d\n",ans);
    return 0;
} 

猜你喜欢

转载自www.cnblogs.com/WR-Eternity/p/9967550.html