【51nod 1086】 背包问题 V2 (多重背包)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sunshiness_s/article/details/82590105

多重背包裸题…

c 表示物品个数, w 表示物品体积, v 表示物品价格,以下不做特殊解释

多重背包的问题,我们可以物品拆开,然后用01背包来求,复杂度是 O ( m c )

但显然这样复杂度比较大,有两种优化:二进制分组、单调队列优化

先来说下二进制分组,也是把物品拆开,只不过不是拆成一个,而是根据二进制拆,因为我们知道1、2、4、8…可以组成所有数。因此把一个物品拆成log份,复杂度为 O ( m l o g c )

而单调队列优化可以优化到 O ( n m )
考虑转移方程 f [ i ] [ j ] = m a x ( f [ i 1 ] [ j k w [ i ] ] + k v [ i ] ) 其中 0 <= k <= m i n ( c [ i ] , j w [ i ] )
a = j / w [ i ] , b = j ,即 j = a w [ i ] + b
转移方程变为 f [ i ] [ w v [ i ] + b ] = m a x ( f [ i 1 ] [ ( a k ) w [ i ] + b ] + k v [ i ] ) 其中 0 <= k <= m i n ( a , c [ i ] )
再令 s = a k ,得到 f [ i ] [ a w [ i ] + b ] = m a x ( f [ i 1 ] [ s w [ i ] + b ] s v [ i ] ) + a v [ i ] 其中 a m i n ( a , c [ i ] ) <= s <= a
因此枚举 b ,用单调队列维护 f [ i 1 ] [ s w [ i ] + b ] s v [ i ] 最大值即可

#include <cstdio>
#include <algorithm>
using namespace std;
#define N 10010
#define mp make_pair
pair<int,int>q[N];
int n,m,w[N],v[N],c[N],f[N*5];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d%d%d",&w[i],&v[i],&c[i]);
    for(int i=1;i<=n;i++){
        c[i]=min(c[i],m/w[i]);
        for(int d=0;d<w[i];d++){    //枚举余数 
            int st=1,ed=0;
            for(int j=0;j<=(m-d)/w[i];j++){
                int tmp=f[j*w[i]+d]-v[i]*j;
                while(st<=ed && tmp>=q[ed].first) ed--;
                while(st<=ed && q[st].second<j-min(c[i],j)) st++;
                q[++ed]=mp(tmp,j);
                f[j*w[i]+d]=q[st].first+j*v[i];
            }
        }
    }
    printf("%d\n",f[m]);
    return 0; 
}

猜你喜欢

转载自blog.csdn.net/sunshiness_s/article/details/82590105