• 洛谷P3188 梦幻岛宝珠


    这道题真的难写啊,细节超级多

    一看就不能直接背包

    考虑到所有的重量都是a*2^b的形式

    所以可以按照数位进行分层dp

    设f[i][j]表示容量j*2^i时最大的价值(此时的状态)

    在每层dp内,只转移b为i的物品

    f[i][j]=max(f[i][j],f[i][j-a]+val)

    每层处理完之后,考虑层间转移

    转移之前,第i层代表的容量为j*2^i,转移之后还应该加上总容量W的前i-1层容量,变为j*2^i+w&((1<<i)-1)

    考虑第i层有(j-k)*2^i的容量装载第i层的物品,那还剩下k*2^i的容量去装载前i-1层的物品

    在第i-1层表示剩余容量k*2^i,需要转化一下:

    当前第i层用了(j-k)*2^i的容量,转化后应该用了j*2^i+w&((1<<i)-1)的容量,减一下,就是前i-1层应该剩下的容量,设为p

    又因为第i-1层的容量表示为x*2^(i-1),所以解一下x*2^(i-1)=p就行了->x=2*k+((w>>(i-1))&1)

    f[i][j]=max(f[i][j],f[i-1][2*k+((w>>(i-1))&1)])

    答案就是最终的f[up][1]了,此时的j=1代表了总容量W

    细节问题:

    在枚举的时候,容量j应该倒序枚举,保证当前只有第i层的物品(因为要枚举当前层物品用了j*2^i的容量)

    呼~

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #include<queue>
    #include<stack>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 1010;
    
    ll n,W,up,ans;
    ll f[110][1100]; // 第 i 位(2^i) , 背包中容量 j*2^i 的最大价值
    ll w[maxn],val[maxn];
    
    vector<int> b[maxn];
    
    ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f;}
    
    int main(){
        while(1){
            n=read(),W=read();
            if(n==-1&&W==-1) break;
            memset(f,0,sizeof(f));
            for(int i=1;i<=n;i++){
                w[i]=read(),val[i]=read();
                int sum=0;
                while(w[i]%2==0) w[i]/=2,sum++;
                for(int j=1000;j>=w[i];j--){
                    f[sum][j]=max(f[sum][j],f[sum][j-w[i]]+val[i]);
                }
            }
    
            up=0;
            for(int i=W;i;i>>=1) up++; up--;
            for(int i=1;i<=up;i++){
                for(int j=1000;j>=0;j--){
                    for(int k=0;k<=j;k++){
                        f[i][j]=max(f[i][j],f[i][j-k]+f[i-1][min(1000ll,k*2+((W>>(i-1))&1))]);
                    } 
                }
            }
            
            printf("%lld\n",f[up][1]);
        }
    
        return 0;
    }
  • 相关阅读:
    二十八、线程安全
    一、JAVA内存区域与内存溢出异常
    一、SQLite学习
    排列问题
    2016年秋季个人阅读计划
    有向图强连通分量求解【转】
    《梦断代码》阅读笔记之五
    《梦断代码》阅读笔记之四
    软件工程个人总结
    《梦断代码》阅读笔记之三
  • 原文地址:https://www.cnblogs.com/tuchen/p/10415034.html
Copyright © 2020-2023  润新知