单调队列优化多重背包

  记得有一个经典的问题:

  有一个容量为$V$的背包,和$n$种物品。第$i$种物品的重量为$w_{i}$,价值为$v_{i}$,数量为$c_{i}$。问背包中装的物品的最大价值和为多少。

Subtask 1 $1\leqslant n\leqslant 100, 1\leqslant V\leqslant 1000,1\leqslant c_{i}\leqslant 100$,答案在32位有符号整数内。

  做法非常简单。so easy~

  设 f[i][j] 表示考虑用前$i$中物品,装容量为$j$的背包可以得到的最大物品价值和。

  转移方程 f[i][j] = max{f[i - 1][j - k * w[i]] + k * v[i]} 。

  于是在$O \left ( nV\sum c_{i} \right )$的时间内解决掉了这个子任务。

Subtask 2 $1\leqslant n\leqslant 1000, 1\leqslant V,c_{i}\leqslant 1000$,答案在32位有符号整数内。

  友善的数据范围。。。只能想办法优化。

  动态规划的常用优化方向:

  • 状态数:状态数已经很优秀了。这样继续优化只能降低空间复杂度但不能降低时间复杂度。
  • 转移:似乎转移的过程中重复对很多数取了最大值。

  例如 f[i - 1][j] 会对 f[i][j],f[i][j + w[i]],f[i][j + w[i] * 2]...... 做出贡献。

  发现要想 f[i - 1][j] 对当前dp值做出贡献,那么一个必要条件是当前枚举的$j‘$在模$w_{i}$意义下与$j$同余。

  所以,考虑阶段$i$的时候可以将dp数组进行分组,对于模$w_{i}$的值为$r$的部分分别跑一次dp。

  然而这有什么卵用呢?

  刚刚考虑了一个dp值对别的dp值的贡献,现在考虑别的dp值对当前考虑的 f[i][j] 的贡献。

  显然它们是: f[i - 1][j], f[i - 1][j - w[i]], f[i - 1][j - w[i] * 2], ..., f[i - 1][j - w[i] * c[i]] 。

  然后对比一下对 f[i][j + w[i]] 的有贡献的状态: f[i - 1][j + w[i]], f[i - 1][j], f[i - 1][j - w[i]], ..., f[i - 1][j - w[i] * (c[i] - 1)] 。

  无非就是多了一项和少了一项的差别。

  现在如何来维护这些这些转移中的最优决策呢?

  设

$j = k\cdot w_{i} + r \ \ \ \ \ \ \ \left( k\in \mathbb{N} , 0\leqslant r < w_{i}\right )$

  那么来看看对 f[i][j] 有贡献的状态都是怎么样转移的:

f[i - 1][j - 0 * w[i]] + 0 * v[i]
f[i - 1][j - 1 * w[i]] + 1 * v[i]
f[i - 1][j - 2 * w[i]] + 2 * v[i]
f[i - 1][j - 3 * w[i]] + 3 * v[i]

  这些可以表示成:

f[i - 1][j - 0 * w[i]] + (0 - k) * v[i] + k * v[i]
f[i - 1][j - 1 * w[i]] + (1 - k) * v[i] + k * v[i]
f[i - 1][j - 2 * w[i]] + (2 - k) * v[i] + k * v[i]
f[i - 1][j - 3 * w[i]] + (3 - k) * v[i] + k * v[i]

  这样,转移的时候就一视同仁了,都是加上$r\cdot v_{i}$,所以只用维护剩余部分的最大值。

  于是我们可以手写堆来在$O \left ( \log n\right )$的时间复杂度内完成插入、删除和查询最大值。

  因此总时间复杂度变为了$O \left ( nV\log n\right )$。

Subtask 3 $1\leqslant n\leqslant 7000, 1\leqslant V,c_{i}\leqslant 7000$,答案在32位有符号整数内。(这评测机速度。。佩服佩服。。)

  友善的数据范围。。。刚刚的做法被卡掉了。。肿么办呢?

  虽然用堆来维护有贡献的转移中的最优解,看似很优秀,但是实际上可以发现仍然有很多状态从进堆到出堆根本就没有机会作为最大值。

  现在来考虑哪些状态一定没有用。

  如果存在一个$j‘$使得$j < j‘$并且$j$和$j‘$在模$w_{i}$意义下同余,同时也满足维护的那一坨的值$g\left(j\right) < g\left(j‘\right)$,那么显然$j$永远不可能在$j‘$之后作为最大值。

  所以每次加入的时候就可以把满足这样性质的前面的状态丢掉。

  这样维护下来的数据结构中的值是单调递减的,所以我们只需要一个队列就行了。

  由于每个状态至多进队1次,出队1次,所以总时间复杂度为$O\left ( nV \right )$

  又因为codevs的评测鸡跑得飞快,然后就过了。

题目传送门 [codevs 5429]

Code

 1 /**
 2  * codevs
 3  * Problem#5429
 4  * Accepted
 5  * Time: 1151ms
 6  * Memory: 492k
 7  */
 8 #include <bits/stdc++.h>
 9 using namespace std;
10 #define pii pair<int, int>
11 #define fi first
12 #define sc second
13
14 int n, V;
15 int *vs, *wss, *cs;
16 int *f;
17 pii *que;
18 int front, rear;
19
20 inline void init() {
21     scanf("%d%d", &n, &V);
22     vs = new int[(n + 1)];
23     wss = new int[(n + 1)];
24     cs = new int[(n + 1)];
25     for(int i = 1; i <= n; i++)
26         scanf("%d%d%d", wss + i, vs + i, cs + i);
27 }
28
29 inline void solve() {
30     f = new int[(V + 1)];
31     que = new pii[(V + 1)];
32     fill(f, f + V + 1, 0);
33     for(int i = 1; i <= n; i++) {
34         for(int r = 0, lim; r < wss[i]; r++) {
35             front = 1, rear = 1, lim = cs[i] * wss[i], que[1] = pii(f[r], r), lim = wss[i] * cs[i];
36             for(int j = r + wss[i], k = 1, g; j <= V; j += wss[i], k++) {
37                 g = f[j] - k * vs[i];
38                 while(front <= rear && que[front].sc + lim < j)    front++;
39                 while(front <= rear && que[rear].fi < g)    rear--;
40                 que[++rear] = pii(g, j);
41                 f[j] = que[front].fi + k * vs[i];
42             }
43         }
44     }
45     printf("%d", f[V]);
46 }
47
48 int main() {
49     init();
50     solve();
51     return 0;
52 }

原文地址:https://www.cnblogs.com/yyf0309/p/8214055.html

时间: 2024-10-14 06:47:42

单调队列优化多重背包的相关文章

背包问题入门(单调队列优化多重背包

背包问题 写这篇文章主要是为了帮帮新人吧,dalao勿喷.qwq 一般的背包问题问法 每种物品都有一个价值w和体积c.//这个就是下面的变量名,请看清再往下看. 你现在有一个背包容积为V,你想用一些物品装背包使得物品总价值最大. 01背包 多种物品,每种物品只有一个.求能获得的最大总价值. 我们考虑是否选择第i件物品时,是需要考虑前i-1件物品对答案的贡献的. 分析 如果我们不选择第i件物品,那我们就相当于是用i-1件物品,填充了体积为v的背包所得到的最优解. 而我们选择第i件物品的时候,我们要

单调队列优化的DP

(持续更新中--) 一.浅谈单调队列之多重背包   前言:首先标题起了一个很优雅的名字,貌似很高深的样子,其实不然,只是把自己理解的记录一下而已. 多重背包的状态转移方程:dp[ i ]  [ j ]  =  max ( dp[ i - 1 ] [ j ]  , dp[ i - 1 ] [ j - k * v[ i ] ] + k * w[ i ]  } (  0 <= k <= num[ i ])  ;  先说一下多重背包的二进制解法,因为大多数的题目都可以用此方法解决:复杂度( num[

0x59 单调队列优化DP

倍增DP太难啦心情好再回去做 poj1821 先让工匠按s排序,f[i][j]表示枚举到第i个工匠涂了j个木板(注意第j个木板不一定要涂) 那么f[i][j]可以直接继承f[i-1][j]和f[i][j-1] 此外 f[i][j]=max(j-l[i]+1<=k<=s[i]){f[i-1][k-1]+(j-k+1)*p} 按照单调队列运用的思想,维护f[i-1][k-1]-k*p的最大值 #include<cstdio> #include<iostream> #incl

使用单调队列优化的 O(nm) 多重背包算法

我搜索了一下,找到了一篇很好的博客,讲的挺详细:链接. 解析 多重背包的最原始的状态转移方程: 令 c[i] = min(num[i], j / v[i]) f[i][j] = max(f[i-1][j-k*v[i]] + k*w[i])     (1 <= k <= c[i])  这里的 k 是指取第 i 种物品 k 件. 如果令 a = j / v[i] , b = j % v[i] 那么 j = a * v[i] + b. 这里用 k 表示的意义改变, k 表示取第 i 种物品的件数比

POJ 1742 Coins 多重背包单调队列优化

http://poj.org/problem?id=1742 题意: 很多硬币,有价值和数量,给出一个上限,问上限内有多少种钱数可以由这些硬币组成. 分析: 好像是楼教主男人八题之一.然后学多重背包单调队列优化时看了别人的程序..所以后来写了就1A了=.= 前一篇小小总结了一下多重背包单调队列优化(http://www.cnblogs.com/james47/p/3894772.html),这里就不写了. 1 #include<cstdio> 2 #include<cstring>

hdu 2191 (多重背包的单调队列优化)

多重背包单调队列优化是思想是.普通的dp为 dp[i][j]=max{dp[i-1][j-k*v[i]]+k*w[i]}; 其实你可以发现对能更新j是j和一个剩余类.也就是 0, v[i],2v[i],3v[i] ,4v[i]... 1 ,1+v[i],1+2v[i],1+3v[i] ........... v[i]-1,2*v[i]-1...... 更新值存在一个剩余类中,组与组之间不存在更新.那么实际上我们可以写dp写好这样 dp[b+x*v[i]]=max{ dp[b+k*v[i]]+(x

[Vijos 1243]生产产品(单调队列优化Dp)

Description 在经过一段时间的经营后,dd_engi的OI商店不满足于从别的供货商那里购买产品放上货架,而要开始自己生产产品了!产品的生产需要M个步骤,每一个步骤都可以在N台机器中的任何一台完成,但生产的步骤必须严格按顺序执行.由于这N台机器的性能不同,它们完成每一个步骤的所需时间也不同.机器i完成第j个步骤的时间为T[i,j].把半成品从一台机器上搬到另一台机器上也需要一定的时间K.同时,为了保证安全和产品的质量,每台机器最多只能连续完成产品的L个步骤.也就是说,如果有一台机器连续完

codevs3327选择数字(单调队列优化)

3327 选择数字 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond 题目描述 Description 给定一行n个非负整数a[1]..a[n].现在你可以选择其中若干个数,但不能有超过k个连续的数字被选择.你的任务是使得选出的数字的和最大. 输入描述 Input Description 第一行两个整数n,k 以下n行,每行一个整数表示a[i]. 输出描述 Output Description 输出一个值表示答案. 样例输入 Sample Input 5 2

HDU 4122 Alice&#39;s mooncake shop 单调队列优化dp

Alice's mooncake shop Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4122 Description The Mid-Autumn Festival, also known as the Moon Festival or Zhongqiu Festival is a popular harvest festival celebrated by Ch