【51Nod1086】背包问题 V2

任务

有N种物品,每种物品的数量为C1,C2……Cn。从中任选若干件放在容量为W的背包里,每种物品的体积为W1,W2……Wn(Wi为整数),与之相对应的价值为P1,P2……Pn(Pi为整数)。求背包能够容纳的最大价值。

第1行,2个整数,N和W中间用空格隔开。N为物品的种类,W为背包的容量。(1 <= N <= 100,1 <= W <= 50000)
第2 - N + 1行,每行3个整数,Wi,Pi和Ci分别是物品体积、价值和数量。(1 <= Wi, Pi <= 10000, 1 <= Ci <= 200)

解法

裸的多重背包跑 O(nwci)
显然这是不被接受的。
徐持衡的论文中《浅谈几类背包题》 O(nmlog) O(nm) 的做法;
这里只介绍 O(nm) 的做法:


f[i] 表示使用了 i 体积,的最大价值。
我们有,

f[j]=max{f[jkwi]+kvi}

我们注意到,在这个方程中, f[j] 实际上是被 f[j1wi],f[j2wi],...,f[jaiwi] 所更新的。
这些 j1wi,j2wi,...,jaiwi mod wi 意义下,余数相同的。
所以我们考虑先枚举余数 d ,然后再枚举+了多少个wi
显然对于不同余数的转移,都是互相独立的。


然后我们观察转移方程,其实与单调队列很相像。
注意到体积的不同,所以我们在加入单调队列的时候,加入的是 f[d+jwi]jvi
时间复杂度为 O(nm)

代码

#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<math.h>
#include<string.h>
using namespace std;
const char* fin="beibao.in";
const char* fout="beibao.out";
const int inf=0x7fffffff;
const int maxn=107,maxm=500007;
int n,m,i,j,k,ans;
int f[maxm],a[maxn],w[maxn],v[maxn];
int b[maxm][2],head,tail;
void add(int xx,int yy){
    while (head<=tail && b[tail][1]<=yy) tail--;
    b[++tail][0]=xx;
    b[tail][1]=yy;
}
int main(){
    freopen(fin,"r",stdin);
    freopen(fout,"w",stdout);
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++) scanf("%d%d%d",&w[i],&v[i],&a[i]);
    for (i=1;i<=n;i++){
        for (j=0;j<w[i];j++){
            head=1;
            tail=0; 
            for (k=0;k<=(m-j)/w[i];k++){
                add(k,f[j+k*w[i]]-k*v[i]);
                if (k-b[head][0]>a[i]) head++;
                f[j+k*w[i]]=b[head][1]+k*v[i];
                ans=max(ans,f[j+k*w[i]]);
            }
        }
    }
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值