bzoj1017 [JSOI2008]魔兽地图DotR——DP

 题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1017

好难想的状态啊!f[i][j][k]表示i号物品有j个向上贡献,一共花了k钱的最大力量;

g[i][j]用在子树中,表示前i个子树花j钱的最大值;

调了半上午,终于发现原来是少看了一个范围,f的第二维的范围不是51而是100啊啊啊啊啊啊!

除此之外此题也有很多要注意的地方,写在注释里了。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int const maxn=55,maxm=2005,inf=1000000000;
int n,m,L[maxn],M[maxn],P[maxn],head[maxn],ct;
int f[maxn][maxn<<1][maxm],g[maxn][maxm],rd[maxn],h[maxn][maxm];
struct N{
    int to,next,w;
    N(int t=0,int n=0,int w=0):to(t),next(n),w(w) {}
}edge[20005];
void dp(int x)
{
    if(!head[x])//叶子 
    {
        L[x]=min(L[x],m/M[x]);//!
        for(int i=0;i<=L[x];i++)//有i个 
            for(int j=0;j<=i;j++)//贡献j个 
                f[x][j][M[x]*i]=P[x]*(i-j);
        return;//!!!!!!!!!!
    }
    L[x]=inf;//!高级装备原本没有限制 
    for(int i=head[x],u;i;i=edge[i].next)
    {
        u=edge[i].to;
        dp(u);
        L[x]=min(L[x],L[u]/edge[i].w);
        M[x]+=M[u]*edge[i].w;//
    }
    L[x]=min(L[x],m/M[x]);
    memset(g,-0x3f,sizeof g);//!!
    g[0][0]=0;//!!
    for(int l=L[x];l>=0;l--)//x物品有l个  //倒序!  //l可以有0个!!!!!!!!!!! 
    {
        int tot=0;
//        memset(g,0,sizeof g);//放在外面! 
        for(int i=head[x],v;i;i=edge[i].next)
        {
            v=edge[i].to;
            tot++;
            for(int j=0;j<=m;j++)//一共有j钱 
                for(int k=0;k<=j;k++)//给v k钱 
                    g[tot][j]=max(g[tot][j],g[tot-1][j-k]+f[v][l*edge[i].w][k]);
                    //g每次不会重新赋初值,所以需要l倒序来保证本次一定可以合成l个x物品 
        }
        for(int j=0;j<=l;j++)
            for(int k=0;k<=m;k++)
                f[x][j][k]=max(f[x][j][k],g[tot][k]+P[x]*(l-j));
    } 
}
int main()
{
    scanf("%d%d",&n,&m);
    char dc;
//    memset(L,0x3f,sizeof L);
    memset(f,-0x3f3f3f3f,sizeof f);
    for(int i=1,x,a,b;i<=n;i++)
    {
        scanf("%d",&P[i]);
        cin>>dc;
        if(dc=='A')
        {
            scanf("%d",&x);
            while(x--)
            {
                scanf("%d%d",&a,&b);
                edge[++ct]=N(a,head[i],b);head[i]=ct;
                rd[a]++;
            }
        }
        else scanf("%d%d",&M[i],&L[i]);
    }
    int tot=0;
    for(int i=1;i<=n;i++)
        if(!rd[i])
        {
            tot++;
            dp(i);
            for(int j=0;j<=m;j++)
                for(int k=0;k<=j;k++)
                    for(int l=0;l<=L[i];l++)
                        h[tot][j]=max(h[tot][j],h[tot-1][k]+f[i][l][j-k]);
        }
    int ans=0;
    for(int j=0;j<=m;j++)ans=max(ans,h[tot][j]);
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2018-06-05 12:37  Zinn  阅读(104)  评论(0编辑  收藏  举报