「51nod 1086」背包问题V2

本文介绍了一种使用单调队列优化多重背包问题的算法。通过将问题分解为针对每个余数的子问题,利用单调队列维护最大价值状态,实现了O(nm)的时间复杂度。代码示例展示了如何在限制条件下计算最大价值。

传送门


problem

有一个 m m m 体积的背包。有 n n n 物品,物品有体积 v i v_i vi,价值 w i w_i wi 和数目 c i c_i ci,求能获得的最大价值。

数据范围: 1 ≤ n ≤ 100 1 \le n \le 100 1n100 1 ≤ m ≤ 5 × 1 0 4 1 \le m \le 5\times10^4 1m5×104 1 ≤ c i ≤ 200 1\le c_i\le200 1ci200


solution

今天 L 一说,我才发现不会单调队列优化多重背包

学一学吧。

考虑普通的背包的转移,有:

f [ i ] = max ⁡ k = 0 min ⁡ ( c i , ⌊ i v i ⌋ ) { f [ i − k v i ] + k w i } f[i]=\max_{k=0}^{\min(c_i,\lfloor\frac{i}{v_i}\rfloor)}\{f[i-kv_i]+kw_i\} f[i]=k=0maxmin(ci,vii){f[ikvi]+kwi}

然后发现,只有在 % v \%v %v 意义下相同的才能转移,那么可以对于每个余数做 d p dp dp

那么( d d d 是余数):

f [ i × v + d ] = max ⁡ k { f [ k × v + d ] + ( i − k ) × w } f [ i × v + d ] = max ⁡ k { f [ k × v + d ] − k w } + i w \begin{aligned} f[i\times v+d]&=\max_k\{f[k\times v+d]+(i-k)\times w\}\\ f[i\times v+d]&=\max_k\{f[k\times v+d]-kw\}+iw \end{aligned} f[i×v+d]f[i×v+d]=kmax{f[k×v+d]+(ik)×w}=kmax{f[k×v+d]kw}+iw

可以用单调队列来维护 f [ k × v + d ] − k w f[k\times v+d]-kw f[k×v+d]kw 的最大值。

时间复杂度 O ( n m ) O(nm) O(nm)


code

#include<bits/stdc++.h>
using namespace std;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=(x+(x<<2)<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
const int N=5e4+5;
int n,m;
int Q[N],f[N],num[N];
int main(){
	n=in(),m=in();
	for(int i=1;i<=n;++i){
		int v=in(),w=in(),c=in();
		for(int d=0;d<v;++d){
			int L=1,R=0;
			for(int j=0;j<=(m-d)/v;++j){
				int tmp=f[j*v+d]-j*w;
				while(L<=R&&Q[R]<=tmp)  R--;
				Q[++R]=tmp,num[R]=j;
				while(L<R&&j-num[L]>c)  L++;
				f[j*v+d]=max(f[j*v+d],Q[L]+j*w);
			}
		}
	}
	printf("%d\n",f[m]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值