2016 USP Try-outs L. The Knapsack problem(超大容量完全背包)

https://codeforces.com/gym/101064/problem/L

 

背包容量S特别大,但是每个物品重量相比之下比较小

 

令mx表示所有物品中重量最大的,把S拆分成两部分,S=A+B 且 |A-B|<=mx

因为如果A和B的重量相差超过mx,可以把mx从重的那一部分放到轻的那一部分

即|A-(S-A)|<=mx

所以 S/2-mx/2 <= A <= S/2+mx/2

同理为了求出 f(S/2-mx/2)到 f(S/2+mx/2)

就要求出 f(S/4- mx*3/4)到 f(S/4+mx*3/4)

这样一直往下

整个mx的偏移范围不会超过[-mx,mx]

所以所有需要求的就是 f(S/2^k-mx)到  f(S/2^k+mx)

状态数为log(S)*mx*2个

每个状态枚举mx的偏移范围平方求即可

 

复杂度为log(S)*mx^2

 

#include<bits/stdc++.h>

using namespace std;

typedef long long LL;

LL dp[101][4001],f[4001]; 
int low[101];
int w[1001],c[1001];

int main()
{
    int T,n,m,mx,cnt;
    
        scanf("%d%d",&n,&m);
        mx=0;
        for(int i=1;i<=n;++i) 
        { 
            scanf("%d%d",&w[i],&c[i]);
            mx=max(mx,w[i]);
        }
        cnt=0;
        while(m)
        {
            low[++cnt]=m-mx;
            m/=2;
        }
        memset(f,0,sizeof(f));
        for(int i=1;i<=n;++i)
            for(int j=w[i];j<=2*mx;++j)
                f[j]=max(f[j],f[j-w[i]]+c[i]);
        for(int i=cnt;i;--i)
            for(int j=low[i];j<=low[i]+mx*2;++j)
                if(j<=2*mx) dp[i][j-low[i]]=f[j];
                else
                    for(int k=(j-mx)/2;k<=j/2;++k)
                        dp[i][j-low[i]]=max(dp[i][j-low[i]],dp[i+1][j-k-low[i+1]]+dp[i+1][k-low[i+1]]);
        printf("%lld",dp[1][mx]);
    
}

 

posted @ 2021-08-10 10:21  TRTTG  阅读(106)  评论(0编辑  收藏  举报