[HNOI2007]梦幻岛宝珠

时间:2019-09-28
本文章向大家介绍[HNOI2007]梦幻岛宝珠,主要包括[HNOI2007]梦幻岛宝珠使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目

题意简洁明了,就是做一个01背包,但是背包的容量\(W\)非常大,并且给出的物品的体积都能表示成\(a\times 2^b,a\leq 10,b\leq 30\)

显然这个\(a\)拿来做背包的体积非常合适,于是我们按照\(b\)分类,设\(dp_{i,j}\)表示只使用\(a\times 2^i\)形式的物品,凑出\(j\times 2^i\)体积的最大价值

其实就是对每一种\(b\)单拎出来做一个01背包

考虑合并掉\(dp\)数组,设\(f_{i,j}\)表示合并出一个形如\(j\times2^i\)并且后\(i-1\)位都不超过\(W\)\(i-1\)位的最大价值

不难发现我们的答案就是\(\max(f_{\log w,0},f_{\log w,1})\)

合并的时候也大力转移,我们枚举一下\(i-1\)位给\(i\)进多少位,则有

\[f_{i,j}=\max_{k=0}f_{i-1,2\times k+w_{i-1}}+dp_{i,k-j}\]

代码

#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
    char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
int a[31][105],b[31][105];
int dp[31][1005],f[31][2005];
int sum[31],cnt[31],n,m,bit[31],tot,S[31];
int main() {
    while(1) {
        scanf("%d%d",&n,&m);
        if(n==-1&&m==-1) return 0;tot=0;
        for(re int i=0;i<=30;++i) cnt[i]=0,S[i]=0;
        memset(dp,0,sizeof(dp));memset(f,0,sizeof(f));
        for(re int w,v,i=1;i<=n;i++) {
            w=read(),v=read();
            for(re int j=0;j<=30;++j) {
                if(w/(1<<j)<=10) {
                    a[j][++cnt[j]]=w/(1<<j),b[j][cnt[j]]=v;
                    S[j]+=a[j][cnt[j]];
                    break;
                }
            }
        }
        for(re int i=0;i<=30;++i)
            for(re int j=1;j<=cnt[i];++j)
                for(re int k=S[i];k>=a[i][j];--k)
                    dp[i][k]=max(dp[i][k],dp[i][k-a[i][j]]+b[i][j]);
        sum[0]=S[0];
        for(re int i=1;i<=30;++i) sum[i]=sum[i-1]/2+1+S[i];
        for(re int i=0;i<=S[0];++i) f[0][i]=dp[0][i];
        while(m) bit[tot++]=(m&1),m>>=1;tot--;
        for(re int i=1;i<=tot;++i) 
            for(re int j=0;j<=sum[i];++j)
                for(re int k=0;k<=j&&k<=S[i];++k) {
                    if(2*(j-k)+bit[i-1]<=sum[i-1]) 
                        f[i][j]=max(f[i][j],dp[i][k]+f[i-1][2*(j-k)+bit[i-1]]);
                    else f[i][j]=max(f[i][j],dp[i][k]+f[i-1][sum[i-1]]);
                }
        int ans=max(f[tot][0],f[tot][1]);
        for(re int i=0;i<tot;++i) 
            ans=max(ans,max(f[i][0],f[i][1]));
        printf("%d\n",ans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/asuldb/p/11603791.html