背包问题及其简单优化总结

 

1.01背包

for(int i = 1;i <= n; i++)

 for(int j = m; j >= w[i]; j--)//如果改成顺序f[i][j]就是由f[i][j-w[i]]推知,不符合题意

        {f[j] = max(f[j] , f[j - w[i]]+v[i]);}

时间复杂度O(mn), 空间o(m)//优化了。也可以二维,f[i][j],二维可用滚动数组来优化(i只与i-1有关)

2.多重背包 规定每种物品固定的个数c[i]

for(int i = 1;i <= n; i++)

for(int j = m; j >= w[i]; j--)

for(int k = 1; k <=c[i] ; k++)

{  if(  j - w[i]*k<0)break;

  f[j] = max(f[j] , f[j - w[i]*k]+v[i]*k);}

时间复杂度O(mn∑c[i])

二进制优化

把 c[i] 分堆 ,分成1,2,4.......2^(K-1),s-2^k+1.// 10 分成1+2+4+3. k=3;

然后把这些堆每堆看成一件物品,用01背包做;时间复杂度O(mn∑log(c[i]))

int n1//堆的数量 
for(int i = 1; i <= n; i++)
{
	cin>>c1>>v1>>w1;//数量,价值,占背包的面积
	int t = 1;
	while(c1 >= t)
	{
		v[++n1] = t*v1;
		w[n1] = t*w1;
		c1 -= t;
		t = t*2; 
	 } 
	 v[++n1] = v1*c1; w[n1] = w1*c1;//剩余部分单独分一堆 10 = 1+2+4+3,,这表示分3的那堆 
}
for(int i = 1;i <= n1; i++)
     for(int j = m; j >= w[i]; j--)
        {f[j] = max(f[j] , f[j - w[i]]+v[i]);}

单调队列优化

元素从队尾进队,把比当前元素更劣的元素从队尾剔除,进队;

把不符合某种要求(在滑动窗口之外)的对首元素剔除;最后用对首 元素来更新f;

     //维护一个单调下降的队列,手写队列..... 
    int head = 0 , tail = -1;
	for(int i = 1; i <= n ;i ++)
	{
		while(head <= tail && q[tail].v <= v[i]) tail --;
		q[++tail].v = v[i]; q[tail].num = i;
		while(head <= tail && q[head].num + k < i)head ++;//k为移动框长度 
	    f[i] = max(f[i],)  
	} //大概长这样因题而异 
 

再来看关于多重背包的优化

对于 f[j]  = max( g[j - w[i]*k]+v[i]*k)//0<=k <= min(c[i],m/w[i])(c[i]数量,w[i]占面积,v[i]价值)(g代表上一轮)

 j mod w[i] 余数 为 r;

那么 j 只能被 mod w[i] 余数为r 的数给更新到;

即 r,r+w[i],r+2*w[i]........

设 j  = n*w[i]+r 

f[n*w[i]+r] = max(g[r +k* w[i] ]+(n-k)*v[i]) = max(f[r+ k*w[i]] - k*v[i])+n*v[i] ;

用单调队列维护f[r+ k*w[i]] - k*v[i]的最值就好

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值