单调队列优化dp 单调队列优化多重背包

单调队列的应用:查询定长区间的最小值或者最大值,相对于RMQ来说是线性级别的算法。

单纯的单调队列实现起来就那两行,相当简单,比较复杂的是用单调队列的定区间查询功能优化dp以及其他算法。

单调队列优化dp的典型例子:

单调队列优化多重背包。

完成这个优化的前提是要对状态转移方程进行一定的变换,这里面包含的思维过程往往比单调队列本身复杂得多。

单调队列优化的多重背包复杂度:O(nm)
考虑把O(c )的转移压缩为O(1)
变形:
a=j/w[i]
b=j%w[i]
j=b+a
w[i]
思路:承重为j时,不考虑ci限制时最多选a个i号元素。假设a个中不选k个,即选a-k个。

f[i][b+0*w[i]]=max(f[i-1][b+0*w[i]]-0*v[i])+0*v[i];
f[i][b+1*w[i]]=max(f[i-1][b+0*w[i]]-0*v[i],f[i-1][b+1*[w[i]])-1*v[i])+1*v[i];
f[i][b+2*w[i]]=max(f[i-1][b+0*w[i]]-0*v[i],f[i-1][b+1*[w[i]])-1*v[i],f[i-1][b+2*w[i]]-2*v[i]])+2*v[i];
....
f[i][b+k*w[i]]=max(f[i-1][b+0*w[i]]-0*v[i],f[i-1][b+1*[w[i]])-1*v[i],f[i-1][b+2*w[i]]-2*v[i]]....f[i-1][b+a*w[i]]-k*v[i])+k*v[i];
....
f[i][b+a*w[i]]=max(f[i-1][b+0*w[i]]-0*v[i],f[i-1][b+1*[w[i]])-1*v[i],f[i-1][b+2*w[i]]-2*v[i]]....f[i-1][b+a*w[i]]-a*v[i])+a*v[i];

还要注意一个细节:对于个数的选择与k无关,而与队列中k的跨度有关,因为选择多少个是j中数值的变化。

再添加一个例题:

这道题咋一看异常抽象完全找不到思路,然而仔细一分析,还是可以通过dp解决的,这就是dp的玄学和神奇之处。

### 单调队列优化多重背包问题的Python实现 对于多重背包问题,当物品数量较大时,朴素解法的时间复杂度过高。为了提高效率,可以采用单调队列优化求解过程[^2]。 #### 原理说明 在处理第`i`种物品时,设该物品的价值为`v[i]`,体积为`w[i]`,最多可以选择的数量为`s[i]`。假设当前考虑容量范围是从`j-w[i]*k`到`j`(其中`0≤k<s[i]+1`),那么在这个范围内选择若干件此物品所获得的最大价值可以通过动态规划的状态转移方程计算得出: \[ dp[j]=\max(dp[j],dp[j-k*w[i]]+k*v[i]) \] 这里存在重复子结构的特点,即随着`k`增加,新的状态总是基于旧的状态更新而来。利用这一点特性,引入滑动窗口最大值的思想——每当遍历一个新的位置`j`时,只需要关注那些能够影响当前位置决策的有效历史信息即可;而这些有效的历史信息正好可以用一个维护着最近若干个元素及其对应索引的双端队列(`deque`)表示出来。 具体来说就是保持一个按顺序存储满足条件的位置列表,并且保证这个列表内的每一个成员都严格优于它后面的成员(这里的优劣关系取决于题目中的定义)。这样做的好处在于每次查询最优解的时候只需访问一次队首元素就能得到答案,从而大大降低了时间开销。 #### Python代码示例 下面给出一段具体的Python代码用于解决上述提到的问题: ```python def multi_pack(n, V, v, w, s): from collections import deque f = [float('-inf')] * (V + 1) f[0] = 0 for i in range(1, n + 1): q = deque() for j in range(V // w[i]): cur_j = min(j * w[i], V) # 清除不在窗口内的元素 while q and q[0][0] < j - s[i]: q.popleft() # 更新f[cur_j] if not q or q[0][1] >= f[cur_j]: f[cur_j] = max(f[cur_j], q[0][1] if q else 0) + v[i] * ((cur_j % w[i] == 0) ^ bool(q)) # 维护单调递减队列 while q and q[-1][1] <= f[cur_j]: q.pop() q.append((j, f[cur_j])) return max(f[V//w[i]*w[i]:]) # 测试数据 n = 3 # 物品数目 V = 8 # 背包总容量 v = [6, 3, 5] # 各类物品单位重量下的价值 w = [2, 2, 3] # 各类物品每件占用的空间大小 s = [4, 2, 3] # 对应种类物品可选数量上限 print(multi_pack(n,V,v,w,s)) ``` 这段程序实现了通过单调队列的方式快速找到每个阶段的最佳方案并最终返回整个过程中所能达到的最大收益值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值