bzoj1017(JSOI2008)魔兽地图

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

钱数很少,所以它也能压进状态里。

还有向上贡献几个物品。所以状态就是第 i 号物品,向上贡献 j 个,总共花 k 元的当前就能得到的力量。

然后可以树形dp。

不同的是平常的树形dp,该点的值就顺便充当前 r 个子树的值;遍历完子树就完成自己的值。

  但这里的状态里有一个“向上贡献 j 个”,不太好弄。所以另开一个 g ,只关注花了多少钱和带来多少力量。

  为了能用这个g转移到dp,不出现花了某些钱其实买不够向上贡献的 j 个当前物品的情况,应该把“总共买 l 个当前物品”放在最外面枚举。

    并且是倒序,为了沿用上一轮的g值。

**不知为何,自己的代码比别人慢了好多!!我觉得没什么不同呀……以后再来看看吧。

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=55,M=2005;const ll INF=0x7fffffff;
int n,m,head[N],xnt,du[N];
ll a[N],L[N],c[N],dp[N][N<<1][M],g[N][M],h[N][M],ans;//dp[][N<<1][]不能dp[][N][] 
struct Edge{
    int next,to;ll w;
    Edge(int n=0,int t=0,ll w=0):next(n),to(t),w(w) {}
}edge[N];
void dfs(int cr)
{
    if(!head[cr])
    {
        L[cr]=min(L[cr],m/c[cr]);
        for(int i=0;i<=L[cr];i++)
            for(int j=0;j<=i;j++)
                dp[cr][j][i*c[cr]]=(i-j)*a[cr];
        return;
    }
    L[cr]=INF;
    for(int i=head[cr],v;i;i=edge[i].next)
    {
        dfs(v=edge[i].to);L[cr]=min(L[cr],L[v]/edge[i].w);
        c[cr]+=c[v]*edge[i].w;        //只是用来限制L[cr] 
    }
    L[cr]=min(L[cr],m/c[cr]);
    memset(g,-2,sizeof g);//g只和子树阶段、钱有关,因为dp涉及"向上贡献几个",所以不方便同时表示前几个子树的力量值 
    g[0][0]=0;
    for(int l=L[cr];l>=0;l--)//g不重赋值,所代表的状态应该足以合成当前的l个当前装备;所以需倒序 
    {
        int tot=0;
        for(int i=head[cr];i;i=edge[i].next)
        {
            tot++;
            for(int k=0;k<=m;k++)//用了k钱
                for(int j=0;j<=k;j++)//j钱给v
                    g[tot][k]=max(g[tot][k],g[tot-1][k-j]+dp[edge[i].to][l*edge[i].w][j]);//k-j钱给之前的子树 
        }
        for(int j=0;j<=l;j++)
            for(int k=0;k<=m;k++)
                dp[cr][j][k]=max(dp[cr][j][k],g[tot][k]+a[cr]*(l-j));//子树们的节余+自己的节余 
    }
}
int main()
{
    scanf("%d%d",&n,&m);char ch;int u,x;ll z;
    memset(L,1,sizeof L);
    memset(dp,-2,sizeof dp);//////
    for(int i=1;i<=n;i++)
    {
        scanf("%lld %c",&a[i],&ch);
        if(ch=='B')
        {
            scanf("%lld%lld",&c[i],&L[i]);
        }
        else
        {
            scanf("%d",&u);
            for(int j=1;j<=u;j++)
            {
                scanf("%d%lld",&x,&z);
                edge[++xnt]=Edge(head[i],x,z);head[i]=xnt;du[x]++;
            }
        }
    }
    int tot=0;
    for(int i=1;i<=n;i++)
        if(!du[i])
        {
            dfs(i);tot++;
            for(int k=0;k<=m;k++)//总共 
                for(int j=0;j<=m;j++)//给之前的树
                    h[tot][k]=max(h[tot][k],h[tot-1][j]+dp[i][0][k-j]);
        }
    for(int i=0;i<=m;i++)ans=max(ans,h[tot][i]);
    printf("%lld",ans);
    return 0;
}

 

posted on 2018-06-05 20:47  Narh  阅读(108)  评论(0编辑  收藏  举报

导航