LeetCode 41 场双周赛 1687. 从仓库到码头运输箱子

算法:单调队列优化 DP

  1. 下文简称最大装货数量 maxBoxesmaxBoxesmaxBoxes 为 mxcmxcmxc,简称最大载重量 maxWeightmaxWeightmaxWeight 为 mxwmxwmxw(二者均大等于 111),w[i]w[i]w[i] 表示前 iii 个物品重量的前缀和(注意 www 数组要开 longlonglong \space longlong long,因为长度 1e51e51e5,每个重量上限也是 1e51e51e5)。

  2. 设 f(i)f(i)f(i) 表示运送前 iii 个物品到指定码头,所需要的最少行程。则很容易想出状态转移方程 f(i)=f(j)+∑j=i−mxci−1cost(j,i)f(i) = f(j) + \sum\limits_{j = i - mxc}^{i - 1}{cost(j, i)}f(i)=f(j)+j=i−mxc∑i−1​cost(j,i) 其中 cost(l,r)cost(l, r)cost(l,r) 表示处理 (l,r](l, r](l,r](或 [l+1,r][l + 1, r][l+1,r]) 区间内的物品所需要的行程,同时上述方程也要满足 w[i]−w[j]⩽mxww[i] - w[j] \leqslant mxww[i]−w[j]⩽mxw

  3. 但是这样看下来,时间复杂度是 O(n2)O(n^2)O(n2),即使用前缀和把 costcostcost 优化成 O(1)O(1)O(1),仍然要先枚举 iii,再枚举 jjj,所以接下来需要想一个能够优化掉一维的方法。首先我们把 costcostcost 的前缀和写出来。由于它需要按照顺序卸货,所以只需要看相邻两个物品是否需要送到同一个码头即可,若是相同的,则两个物品可以在同一码头被同一时间放下,否则就需要多一次行程。于是我们用 c(x)c(x)c(x) 表示第 x−1x - 1x−1 个物品与第 xxx 个物品是否在相同码头卸货,若不同则为 111,相同则为 000,将其求前缀和,用s(x)s(x)s(x) 表示。这里有一个边界条件 —— 若 cost(l,r)cost(l, r)cost(l,r) 中的 lll 满足 s(l+1)>s(l)s(l + 1) > s(l)s(l+1)>s(l) 说明从 lll 到 l+1l + 1l+1 是需要多一次行程变码头的,但是此时的 l+1l + 1l+1 才是我们处理最后一段的起点,意味着我们可以直接从货舱到达 l+1l + 1l+1 所在的码头,即不需要算上 c(l+1)c(l + 1)c(l+1)。特别地,对于第一个物品,我们不做任何处理,因为一开始车就在货舱,可以直接到达第一个物品所在的码头,即 s(1)=s(0)=0s(1) = s(0) = 0s(1)=s(0)=0 而每一次从货舱出发和卸完所有货回到货舱也要花两次行程,综合起来,costcostcost 的返回值如下:cost(l,r)=s[r]−s[l]+2−(s[l+1]>s[l])cost(l, r) = s[r] - s[l] + 2 - (s[l + 1] \space \gt s[l])cost(l,r)=s[r]−s[l]+2−(s[l+1] >s[l]) 下文中用 check(x)check(x)check(x) 表示 s(x+1)>s(x)s(x + 1) \gt s(x)s(x+1)>s(x) 是否成立。

  4. 有了前缀和优化,我们将公式写开,就变成以下两种形式 f(i)=f(j)+[s(i)−s(j)−check(j)]+2(1)f(i) = f(j) + [s(i) - s(j) - check(j)] + 2 \tag {1}f(i)=f(j)+[s(i)−s(j)−check(j)]+2(1) f(i)=s(i)+[f(j)−s(j)−check(j)]+2(2)f(i) = s(i) + [f(j) - s(j) - check(j)] + 2 \tag {2}f(i)=s(i)+[f(j)−s(j)−check(j)]+2(2) 而通过对公式 (2)(2)(2) 的观察,我们发现,中括号里的内容其实就是在求 min⁡1⩽j⩽i−1f(j)−s(j)−check(j)\min\limits_{1 \leqslant j \leqslant i - 1}{f(j) - s(j) - check(j)}1⩽j⩽i−1min​f(j)−s(j)−check(j) 而 s(i)s(i)s(i) 是定值,这样就能够把这个问题转化为单调队列问题,即在一次遍历 iii 从 111 到 nnn 的过程中,不断求出 iii 之前的一个表达式的最值。

  5. 单调队列的实现。注意刚开始时,不能把队列置为空,因为这样就没法更新 f(1)f(1)f(1),会造成错误的后续更新,所以一开始要在队尾加入元素 000(在代码上不用表示,初值即为 000)。而初始值为 000 的原因有以下两点 —— 一是由公式可知,能更新 f(1)f(1)f(1) 的只有 f(0)f(0)f(0),而一定有 f(0)−s(0)−check(0)=0;f(0) - s(0) - check(0) = 0;f(0)−s(0)−check(0)=0; 二是要满足两个条件 mxcmxcmxc 和 mxwmxwmxw,当初始队头元素为 000 时,一定有 1−0<=mxc,w[1]−w[0]=weight[1]⩽mxw1 - 0 <= mxc, \space \space w[1] - w[0] = weight[1] \leqslant mxw1−0<=mxc,  w[1]−w[0]=weight[1]⩽mxw(根据题目条件所说,每个物品的重量都不会超过最大载重量),这样不会导致弹出队头元素把队列变空。接着求能更新当前 iii 的 jjj 时,先将 i−q.front()>mxc或w[i]−w[q.front()]>mxwi - q.front() \gt mxc \space \space 或 \space \space w[i] - w[q.front()] \gt mxwi−q.front()>mxc  或  w[i]−w[q.front()]>mxw 的队头元素弹出,这是为了满足最后一段要处理的区间满足题目的数量和重量要求,接下来就是更新 f(i)f(i)f(i),这里更新采用公式 (1)(1)(1),因为 costcostcost 函数上面已经实现过,写起来比较简洁;最后就是将当前元素插入队尾,这步操作可以是保持严格单调递增,也可以保持单调不减,这个看你心情(逃),因为公式中的 s(i)s(i)s(i) 对于每次枚举到的 iii 来说都是定值。

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

C ++ 代码

#define fup(i, a, b) for (int i = a; i <= b; i ++ )
#define fdn(i, a, b) for (int i = a; i >= b; i -- )typedef long long LL;const int N = 100010;class Solution {public:int f[N], s[N];int q[N], hh, tt;LL w[N];bool check(int x) {return s[x + 1] > s[x];}int cost(int l, int r) {return s[r] - s[l] + 2 - check(l);}int boxDelivering(vector<vector<int>>& b, int tot, int mxc, int mxw) {int n = b.size();fup(i, 1, n) {int x = b[i - 1][0], y = b[i - 1][1];w[i] = w[i - 1] + y;if (i >= 2) s[i] = s[i - 1] + (x != b[i - 2][0]);}fup(i, 1, n) {while (hh <= tt && (i - q[hh] > mxc || w[i] - w[q[hh]] > mxw)) hh ++ ;if (hh <= tt) f[i] = f[q[hh]] + cost(q[hh], i);while (hh <= tt && f[q[tt]] - s[q[tt]] - check(q[tt]) >= f[i] - s[i] - check(i)) tt -- ;q[ ++ tt] = i;}return f[n];}
};

【单调队列优化 DP】相关推荐

  1. 算法笔记--单调队列优化dp

    单调队列:队列中元素单调递增或递减,可以用双端队列实现(deque),队列的前面和后面都可以入队出队. 单调队列优化dp: 问题引入: dp[i] = min( a[j] ) ,i-m < j ...

  2. poj 2373(单调队列优化dp)

    在长为L(<=1000000)的草地(可看成线段)上装喷水头,喷射是以这个喷水头为中心,喷水头的喷洒半径是可调节的调节范围为[a,b].要求草地的每个点被且只被一个喷水头覆盖,并且有些连续区间必 ...

  3. poj 1821(单调队列优化dp)

    题意:有一道线性篱笆由N个连续的木板组成.有K个工人,你要叫他们给木板涂色.每个工人有3个参数:L 表示 这个工人可以涂的最大木板数目,S表示这个工人站在哪一块木板,P表示这个工人每涂一个木板可以得到 ...

  4. 洛谷P3195 [HNOI2008]玩具装箱TOY(单调队列优化DP)

    题目描述 P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一种特殊的一维容器中.P教授有编号为1...N的N件玩具, ...

  5. 【计蒜客 - 蓝桥训练】蒜厂年会(单调队列优化dp,循环数列的最大子段和)

    题干: 在蒜厂年会上有一个抽奖,在一个环形的桌子上,有 nn 个纸团,每个纸团上写一个数字,表示你可以获得多少蒜币.但是这个游戏比较坑,里面竟然有负数,表示你要支付多少蒜币.因为这些数字都是可见的,所 ...

  6. 单调队列以及单调队列优化DP

    单调队列定义: 其实单调队列就是一种队列内的元素有单调性的队列,因为其单调性所以经常会被用来维护区间最值或者降低DP的维数已达到降维来减少空间及时间的目的. 单调队列的一般应用: 1.维护区间最值 2 ...

  7. POJ 1821 Fence(单调队列优化DP)

    题解 以前做过很多单调队列优化DP的题. 这个题有一点不同是对于有的状态可以转移,有的状态不能转移. 然后一堆边界和注意点.导致写起来就很难受. 然后状态也比较难定义. dp[i][j]代表前i个人涂 ...

  8. 【单调队列优化DP】烽火传递 LibreOJ - 10180

    题目来源 点我进入提交题目 反思 因为目前在学习单调队列优化DP,所以会往单调队列上面想.然后犯了一个错误就是,认为这个题目只要用单调队列就可以完成,单调队列只是用来减少时间复杂度的,遇到了求最优解的 ...

  9. AcWing 1089 烽火传递 题解(动态规划—DP—单调队列优化DP)

    AcWing 1089 烽火传递 单调队列优化DP,思路比较简单,维护一个保持元素单调递增的单调队列,队首就是第i座烽火台能接收到的,代价最小的方案,加上第i座烽火台的代价就是这座烽火台的最小值 #i ...

  10. 【单调队列优化dp】jzoj4883灵知的太阳信仰 纪中集训提高B组

    [NOIP2016提高A组集训第12场11.10]灵知的太阳信仰 (File IO): input:array.in output:array.out Time Limits: 1000 ms Mem ...

最新文章

  1. Java 8按属性区分
  2. 微软图像加密算法被破解,谷歌等大厂都在用,MIT硕士小哥开源逆向所用方法...
  3. C语言 | 编程实现6
  4. 收集计算机网络经典的面试题
  5. mysql创建表格1warning_MySQL:创建、修改和删除表
  6. 利用python批量修改文件名称
  7. 7-37 整数分解为若干项之和 (20 分)(dfs)
  8. php 检查语法命令,php语法检查的方法有哪些?(代码示例)
  9. protobuf-3.0 win环境编译
  10. Linux在日志中搜索关键词
  11. 记录几个贴代码比较美观的网站
  12. edius隐藏快捷键_edius8常用快捷键有哪些|edius快捷键使用大全汇总 - 软件教程 - 格子啦...
  13. 数学符号Span的含义
  14. 烙铁使用注意事项及元器件件焊接要点
  15. a0图框标题栏尺寸_机械制图140标题栏-机械制图a0标题栏-机械制图图框标题栏标准...
  16. 将自己常用的Ubuntu18.04备份为ISO文件,保留私人配置与文件
  17. win10html服务器搭建,windows10 系统配置nginx文件服务器的图文教程
  18. H5新标签 figcaption 定义图文并茂的html5新标签-figure、figcaption
  19. python自相关函数提取基音周期_基音周期的提取
  20. IBMX3650M4服务器重装window系统

热门文章

  1. 浙江省计算机三级证书怎么查,浙江省计算机三级成绩怎么查询?
  2. iOS 仿淘宝,上拉进入详情页面
  3. html 隐藏整个fieldset,HTML fieldset disabled 属性 | 菜鸟教程
  4. linux mpeg4ip 编译,[操作系统]CentOS6.2下编译mpeg4ip
  5. Linux打假,中国为什么需要Linux?
  6. Oracle定义约束 外键约束
  7. 【Redis】精品面试题100道
  8. server2003进入oracle,[转载]在windows server 2003平台上安装Oracle 11G图解
  9. 永中“闹鬼”,天理不容
  10. 读懂正则表达式并学会简单的运用