多重背包 单调队列优化

多重背包 单调队列优化

首先根据我们在前面分析的多重背包和完全背包的状态计算可以知道,多重背包只能选有限个,而完全背包可以选无限个,他们俩的上限一个是枚举到上限为止,一个是枚举到不能放了为止。

在这里插入图片描述

那么根据这个图,我们是可以根据单调队列来解决这个问题的

f[j] 是由所有同余与 v 的值转移过来的,比如现在有一个物品 v 是3,那么f[0],f[1],f[2]是三个不同的同余类,对于f[0], f[3], f[6], f[9]是一个同余类

注意上面的图,每一个滑动窗口的原始值都比原来多 w


参考了三篇blog进行整合:

https://www.acwing.com/solution/content/6500/

https://www.acwing.com/solution/content/1537/

https://www.acwing.com/solution/content/4237/

算是理解了但是我好像不是很会表述呜呜。

// Problem: 多重背包问题 III
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/6/
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// Code by: ING__
// 
// Edited on 2021-07-24 16:05:22

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
#define ll long long
#define re return
#define Endl "\n"
#define endl "\n"

using namespace std;

typedef pair<int, int> PII;

int dx[4] = {-1,0,1,0};
int dy[4] = {0,1,0,-1};

int n, m;
int q[20010];
int f[20010];
int g[20010];

int main(){
	cin >> n >> m;
	
	for(int i = 1; i <= n; i++){
		memcpy(g, f, sizeof(f));
		
		int s, v, w;
		cin >> v >> w >> s;
		for(int j = 0; j < v; j++){ // 枚举余数
			int hh = 0;
			int tt = -1;
			
			for(int k = j; k <= m; k += v){
				if(hh <= tt && q[hh] < k - s * v) // 单调队列,如果超过范围了,就要及时弹出
                    // 我们这里的范围就是前 s 个
                    // 但是我们这里因为是枚举的余数,所以要进行变换,即看看现在我要放进队列的是哪几个 v ,现在的区间范围是多少
					hh++;
				while(hh <= tt && g[q[tt]] - (q[tt] - j) / v * w <= g[k] - (k - j) / v * w)
                    // 我们里面存的都是加过 w 的,我们要把它们减回来来比较在队列里是不是应该加回来
                    // 我们要减回来才能进行同一相对大小的比较
                    // 为什么要减参考最后一个链接前面三四端
					tt--;
				if(hh <= tt)
					f[k] = max(f[k], g[q[hh]] + (k - q[hh]) / v * w);
					// 看中间空余了多少的体积;我们在更新的时候,我们要加的实际上应该是 (k - j) / v + w
					// 但是我们在前面已经加过了这些,我们如果这时候再加这些就重复了
					// 所以我们实际上要加的只是中间没加过的而已
				q[++tt] = k;
			}
		}
	}
	
	cout << f[m];
	
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值