梦幻岛宝珠


首先值的说明的是这道题在牛客上过了,但在洛谷上没有过(一半TLE),可能洛谷的数据比较叼

  1. 所以可以将vector去掉,简化数据存储方式,减少不必要的存储方式
  2. make the upper limit of j more accurate and also smaller by sumw

至于题目的思路,这道题暗示(明示)的很明确了,一是利用2^i 分离解决,二是利用这个数据整合出最终答案,这个比较难:

首先,对于第二部分(g)肯定是要将前i组整合在一起,那么就需要考虑j的意义,j既要能够利用f(i,j)--> j*2^i , 又要能够逐渐趋近于真正的上限maxw,那么别人的思路j*2^i+maxw&(2^u-1)就十分巧妙,然后1*2^len+w&(2^len-1)就是真正的上限,那么值得我学习的就是maxw&(2^len-1)这种得到低len位的方式,以及能够逐渐趋近于真实值的思想,还有就是将数永远看做二进制的形式就能启发思维,甚至简化复杂度
另外,在g的过程中,k的使用就有种暴力的感觉,因为我们很难精确化k , 这时候就要分析复杂度,看是否需要和可能使用暴力
最后,这个题虽然是暗示,其实以后遇到数据量过大的也可以考虑使用这种类似与ST表的思想或者分块的思想来通过预处理降低时间复杂度

the code before optimizing:

//https://ac.nowcoder.com/acm/problem/20058
#include
#include
#include
#include
#define LOCAL   
using namespace std;
const int maxn=101;
int w[maxn];    //save the a in a*2^b of id (1~n)
int v[maxn];    //save the value of id 
int f[31][1010];  // for the group of 2^i, we deal with it by 01 backpack
int g[31][1010];  // for the i group, we do 01pack , and j--> j*2^i + maxw&(2^i-1)
vector grp[31];//save the id which has the same b in a*2^b


int read(){
    int s=0,f=1,c=getchar();
    while(c<'0' || c>'9') { if (c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9') {s=s*10+c-'0';c=getchar();}
    return f*s;
}

int main(){
    #ifdef LOCAL
    freopen("input.txt","r",stdin);
    #endif
    int n=read(),maxw=read();
    int len=31;
    while(maxw<=(1<<(--len)));
    // cout<>=1;cont++;} the following code is better
            while(wgt%(1<<(--cont)));
            grp[cont].push_back(i);
            w[i]=wgt/(1<=w[grp[b][i]];--j)
                        f[b][j]=max(f[b][j],f[b][j-w[grp[b][i]]]+v[grp[b][i]]);
        // cout<>(b-1))&1))]);
        printf("%d\n",g[len][1]);
        n=read();maxw=read();
        // tmpw=maxw;
        // len=0;
        // while(tmpw>1) {tmpw>>=1;len++;}
        len=31;
        while(maxw<=(1<<(--len)));
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        memset(w,0,sizeof(w));
        memset(v,0,sizeof(v));
        for (int i=0;i<31;++i) grp[i].clear();
    }
    return 0;
}

the optimized code :

//https://ac.nowcoder.com/acm/problem/20058
#include
#include
#include
#include
#define LOCAL   
using namespace std;
const int maxn=101;
int sumw[31];
int w[31][maxn];
int v[31][maxn];    //save the value of id 
int f[31][1010];  // for the group of 2^i, we deal with it by 01 backpack
int g[31][1010];  // for the i group, we do 01pack , and j--> j*2^i + maxw&(2^i-1)
int num[31];


int read(){
    int s=0,f=1,c=getchar();
    while(c<'0' || c>'9') { if (c=='-') f=-1;c=getchar();}
    while(c>='0' && c<='9') {s=s*10+c-'0';c=getchar();}
    return f*s;
}

int main(){
    #ifdef LOCAL
    freopen("input.txt","r",stdin);
    #endif
    int n=read(),maxw=read();
    int len=31;
    while(maxw<=(1<<(--len)));
    while(n!=-1){
        for (int i=1;i<=n;++i){
            int wgt=read();
            int cont=31;
            while(wgt%(1<<(--cont)));
            // sumw[cont]+=(w[cont][++num[cont]]=wgt/(1<=w[b][i];--j)
                for (int j=sumw[b];j>=w[b][i];--j)
                        f[b][j]=max(f[b][j],f[b][j-w[b][i]]+v[b][i]);
        // for (int i=0;i<=10*num[0];++i) g[0][i]=f[0][i]; 
        for (int i=0;i<=sumw[0];++i) g[0][i]=f[0][i]; 
        for (int b=1;b<=len;++b){
            sumw[b]+=(sumw[b-1]+1)>>1;   // pay attention to the change of /2
            // for (int j=0;j<=10*n;++j)
            int tmp=((maxw>>(b-1))&1);
            for (int j=sumw[b];j>=0;--j)
                for (int k=0;k<=j;++k)
                    g[b][j]=max(g[b][j],f[b][j-k]+g[b-1][min(sumw[b-1],2*k|tmp)]); // even number + 1/0 = even number | 1/0
        }
        // for (int b=1;b<=len;++b)
        //     for (int j=0;j<=10*n;++j)
        //         for (int k=0;k<=j;++k)
        //             g[b][j]=max(g[b][j],f[b][j-k]+g[b-1][min(10*n,2*k+((maxw>>(b-1))&1))]);
        printf("%d\n",g[len][1]);
        n=read();maxw=read();
        len=31;
        while(maxw<=(1<<(--len)));
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        memset(w,0,sizeof(w));
        memset(v,0,sizeof(v));
        memset(num,0,sizeof(num));
        memset(sumw,0,sizeof(sumw));
    }
    return 0;
}