[bzoj1017] [JSOI2008] 魔兽地图

题目链接

树上背包。

(f[i][j][k])表示(i)节点有(j)个来合成高一级的装备,买(i)节点及其子树的装备共花了不大于(k)元可以得到的最大力量值。

对于每一颗树做(tree~dp)转移,如果是叶子节点就直接算,

否则先根据儿子的限制以及钱的限制处理出最多可以买几个,然后枚举当前这件装备做几个,设(g[j])为花(j)元做(i)件当前的装备之后剩下的边角料的最大获利,这个可以枚举儿子之后类似于背包的转移。之后(f)根据(g)算一遍就行了。

#include<bits/stdc++.h>
using namespace std;
inline void read(int &x) {
    int f=1;x=0;char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') f-=f;
    for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);x*=f;
}
inline void print(int x) {
    if(x<0) putchar('-'),x=-x;
    if(!x) return ;
    print(x/10);putchar((x%10)^48);
}
inline void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}
int f[60][120][2005],g[2005],n,head[60],tot,m,v[120],c[120],lim[120],vis[120],ans[2001],deg[120];
struct edge{int to,nxt,w;}e[120];
void ins(int u,int v,int w) {e[++tot].to=v,e[tot].nxt=head[u],head[u]=tot,e[tot].w=w;}
void dfs(int x) {
    if(vis[x]) return ;vis[x]=1;
    if(!head[x]) {
        lim[x]=min(lim[x],m/c[x]);
        for(int i=lim[x];~i;i--)
            for(int j=i;j<=lim[x];j++)
                f[x][i][j*c[x]]=v[x]*(j-i);
        return ;
    }lim[x]=1e9;
    for(int i=head[x];i;i=e[i].nxt) {
        dfs(e[i].to);
        lim[x]=min(lim[x],lim[e[i].to]/e[i].w);
        c[x]+=c[e[i].to]*e[i].w;
    }
    lim[x]=min(lim[x],m/c[x]);
    for(int i=lim[x];~i;i--) {
        memset(g,-0x3f,sizeof g);g[0]=0;
        for(int j=head[x];j;j=e[j].nxt)
            for(int k=m;~k;k--) {
                int t=-1e9;
                for(int p=0;p<=k;p++)
                    t=max(t,g[k-p]+f[e[j].to][e[j].w*i][p]);
                g[k]=t;
            }
        for(int j=0;j<=i;j++)
            for(int k=0;k<=m;k++)
                f[x][j][k]=max(f[x][j][k],g[k]+(i-j)*v[x]);
    }
}
int main() {
    read(n);read(m);memset(f,-0x3f,sizeof f);
    for(int i=1;i<=n;i++) {
        read(v[i]);char ch[2];scanf("%s",ch+1);
        if(ch[1]=='A') {
            int T;read(T);int x,y;
            for(int j=1;j<=T;j++) read(x),read(y),ins(i,x,y),deg[x]++;
        }else read(c[i]),read(lim[i]);
    }
    for(int i=1;i<=n;i++)
        if(!deg[i]) {
            dfs(i);//write(i);
            for(int j=m;~j;j--)
                for(int k=0;k<=j;k++)
                    ans[j]=max(ans[j],ans[j-k]+f[i][0][k]);
        }
    write(ans[m]);
    return 0;
}

原文地址:https://www.cnblogs.com/hbyer/p/9832179.html