数据结构优化dp
滚动数组
在dp时经常会发现只有相邻阶段间状态才会有直接联系,在转移方程中的体现形如:只有前 \(m\) 个阶段能影响当前阶段的状态,因此我们不需要储存下 \(n\) 个阶段的所有状态,只需要储存 \(m\) 个阶段的状态,以做到优化存储空间的目的。
用这种方法可以将dp某一维干掉,把 \(\mathcal O(N)\) 的空间复杂度优化到常数
值域树状数组/平衡树优化dp
考虑dp方程
可以将 \(h\) 数组离散化然后用树状数组维护,也可以维护平衡树来动态支持 \(h[i]\) 的插入删除
前缀和优化
略
单调队列
当你发现题目的转移满足这种形式时
也就是滑动窗口,使用单调队列是最好的选择
有些题目的转移式写出来可能不是这个形式,但是通过移项拆分,将相同变量的放到一起,可以凑出这种形式,具体可以参考后面标上单调队列建模标签的题目
ybtoj 金牌导航 递增子序列 (值域树状数组)
题目
给定 \(n\) 个数字组成的序列,问长度为 \(m\) 的严格上升子序列有多少个
\(1\le n\le 10^4,1\le m\le 100\)
题解
首先将数字离散化,然后进行dp
设 \(f[i][j]\) 表示以第 \(i\) 个元素结尾的长度为 \(j\) 的上升子序列的个数,那么有
然后你发现你要求的是值域上的一段区间,所以考虑使用树状数组优化即可
P2605 [ZJOI2010]基站选址 (线段树)
题目
有 \(N\) 个村庄坐落在一条直线上,第 \(i(i>1)\) 个村庄距离第 \(1\) 个村庄的距离为 \(D_i\)。需要在这些村庄中建立不超过 \(K\) 个通讯基站,在第 \(i\) 个村庄建立基站的费用为 \(C_i\)。如果在距离第 \(i\) 个村庄不超过 \(S_i\) 的范围内建立了一个通讯基站,那么就村庄被基站覆盖了。如果第 \(i\) 个村庄没有被覆盖,则需要向他们补偿,费用为 \(W_i\)。现在的问题是,选择基站的位置,使得总费用最小。
\(K\leq N\),\(K\leq 100\),\(N\leq 20,000\),\(D_i \leq 1000000000\),\(C_i\leq 10000\),\(S_i \leq1000000000\),\(W_i \leq 10000\)。
题解
设 \(f[i][j]\) 表示第 \(j\) 个基站建在 \(i\) 村庄的最小费用,那么有转移
其中 \(\text{cost}[i][j]\) 表示村庄 \(i\) 到村庄 \(j\) 的赔偿费用
由于 \(j\) 这一维连续,那么这一维可以删掉,转移方程可以改写成
这个题妙的地方来了,这里单独计算 \(\text{cost}[i][j]\) 并不容易,于是我们可以将所有 \(\text{cost}\) 代表的花费全都加在 \(f\) 数组中
具体来说,我们通过二分找出第 \(i\) 个村庄的范围是 \([l_i,r_i]\),那么我们在转移 \(i\) 的时候,如果上一个基站建在了 \([1,l_i)\) 中,那么我们就需要给 \(i\) 支付赔偿费用,所以我们可以直接将 \(i\) 的赔偿费用,加在 \(f\) 数组中,我们可以维护一个线段树支持区间加区间求 \(\min\) 就解决了这道题
时间复杂度 \(\mathcal O(nk\log n)\)
BZOJ3688 折线统计 (值域树状数组)
题解
先将点按照横坐标大小排序
设 \(f[i][j][0/1]\) 表示考虑到第 \(i\) 个点,当前已经有了 \(j\) 条折线,当前折线是上升还是下降
转移如下
然后值域树状数组优化就做完了
时间复杂度 \(O(nk\log n)\)
P6089 [JSOI2015]非诚勿扰 (与极限有关的推导和树状数组求逆序对)
题目
一共有 \(N\) 位客人和 \(M\) 本书,他们都希望能够找到一本适合自己的书。
\(1\sim N\) 的书都被编上了一个号码,并且每本书的编号不一样。
每个客人都有一个选书列表,对于每一本书,他有 \(p\) 的概率选择这本书(换言之,会以 \(1-p\) 的概率拒绝这本书)。
如果他拒绝这本书,他就会去看下一本书,如果他不选最后一本书,则他又会从第一本书开始选书,直到他找到一本适合自己的书。
当然,两个客人可能选到同一本书。
在考虑任意两个不同的客人 \(a\) 和 \(b\) ,如果 \(a\) 的编号比 \(b\) 的编号小,而 \(a\) 选择的书的编号比 \(b\) 选择的编号大,那么 \(a\) 和 \(b\) 就叫做一对不稳定因素。
店主希望求出不稳定因素的期望数目。
\(1\le N,M\le 5\times 10^5\)
题解
考虑一个人在它的列表中选第1本书的概率。假设他的列表长度为 \(k\)
即
然后可以推出 \(g(i)=g(i-1)\times (1-p)\)
最后值域树状数组求逆序对即可
时间复杂度 \(\mathcal O(M\log N)\)
P7302 [NOI1998] 免费的馅饼 (排序+值域树状数组)
题解
设 \(f[i]\) 表示考虑完前 \(i\) 个馅饼时的最大分数
转移方程为
其中
对绝对值分类讨论
第一个条件排序干掉,第二个条件离散化后使用值域树状数组优化,这样就可以保证同时满足两个条件
时间复杂度为 \(O(n\log n)\)
P3287 [SCOI2014]方伯伯的玉米田 (二维树状数组)
题目
方伯伯在自己的农田边散步,他突然发现田里的一排玉米非常的不美。这排玉米一共有 \(N\) 株,它们的高度参差不齐。方伯伯认为单调不下降序列很美,所以他决定先把一些玉米拔高,再把破坏美感的玉米拔除掉,使得剩下的玉米的高度构成一个单调不下降序列。方伯伯可以选择一个区间,把这个区间的玉米全部拔高 \(1\) 单位高度,他可以进行最多 \(K\) 次这样的操作。拔玉米则可以随意选择一个集合的玉米拔掉。问能最多剩多少株玉米,来构成一排美丽的玉米。
$2 \le N \lt 10^4 $,\(2 \le K \le 500\),\(1 \leq a_i \leq 5000\)。
题解
拔高玉米有一个显然的贪心策略:每次拔的区间都形如 \([i,n]\),因为这样不会使得答案变劣
然后既然我们要拔高 \(i\) ,那么 \(i\) 就一定要在最终的答案序列中,不然就拔了个寂寞
设 \(f[i][j]\) 表示LIS以 \(i\) 为结尾,\(i\) 被拔高了 \(j\) 次最长不降子序列的长度,转移有
然后我们发现这东西有三维限制,我们先 \(\text{for}\) \(i\),这一维,然后利用二维树状数组进行计算
时间复杂度 \(\mathcal O(nk\log n\log k)\)
P2467 [SDOI2010]地精部落 (滚动数组+前缀和)
题目
给 \(1\) 到 \(n\) 进行排列,排列后对于任意一个数,两边的数都比它大,或都比它小(波动序列)。求方案数
\(n\le 5000\)
题解
这种排列的题都可以往求错排数的方向上想一想
设 \(f[i][j][0/1]\) 表示长度为 \(i\) 的排列结尾是 \(j\) 且 \(j\) 是山峰/山谷的方案数
考虑转移,末尾可以再填一个 \(1\sim i+1\) 之间的数,假设我们填的这个数是 \(j\),那么有转移
滚动数组滚掉第一位维,前缀和优化第二维即可
时间复杂度 \(\mathcal O(n^2)\)
ybtoj 金牌导航 最大连续和 (单调队列)
题目
给定一个长度为 \(n\) 的序列 \(a\) ,要求从中找到一段连续的长度不超过 \(m\) 的子序列,使得这个序列的和最大, \(a\) 可以为负
\(n,m\le 2\times 10^6\)
题解
设 \(f[i]\) 表示以 \(a[i]\) 结尾长度不超过 \(m\) 的最大子序列和,那么有转移
首先使用前缀和优化掉求和,我们把前缀和存在 \(\text{pre}\) 数组中,那么转移式变为
那么后面的那个 \(\min\) 就变成了滑动窗口的形式,单调队列优化即可
时间复杂度 \(\mathcal O(n)\)
ybtoj 金牌导航 修剪草坪 (单调队列)
题目
有一个长度为 \(N\) 的数列,我们现在要从序列中选取一些数,但是不能连续取超过 \(K\) 个数,问选取的数字之和最大是多少
\(1\le N\le 1\times 10^5\)
题解
设 \(f[i][0/1]\) 表示考虑到第 \(i\) 个数,当前数不选/选时的最大和,转移方程如下
单调队列优化即可
时间复杂度 \(\mathcal O(n)\)
ybtoj 金牌导航 旅行问题 (单调队列建模)
题目
John 打算驾驶一辆汽车周游一个环形公路。公路上总共有 \(n\) 车站,每站都有若干升汽油(有的站可能油量为零),每升油可以让汽车行驶一千米。John 必须从某个车站出发,一直按顺时针(或逆时针)方向走遍所有的车站,并回到起点。在一开始的时候,汽车内油量为零,John 每到一个车站就把该站所有的油都带上(起点站亦是如此),行驶过程中不能出现没有油的情况。
判断以每个车站为起点能否按条件成功周游一周。
\(3 \le n\le 10^6\)
题解
先破环成链,链的长度为 \(2n\) 然后考虑顺时针的情况
对汽车油量和距离分别做前缀和 \(s_1, s_2\),如果从第 \(i\) 个点出发可以回到起点,就意味着在 \([i, i+n-1]\) 的区间内,满足所有油量与距离之差的前缀和都大于等于 \(0\),即满足
这个东西是很难维护的,所以我们可以尝试对汽车油量与距离之差做前缀和,记为 \(s\)。
算出 \(s\) 数组后,我们就可以求区间最小值减去 \(s[i-1]\) 与 \(0\) 作比较,问题转化成了滑动窗口
逆时针同理,时间复杂度 \(\mathcal O(n)\)
单调队列优化多重背包 (单调队列建模)
题目
我们现在有 \(n\) 个物品,第 \(i\) 物品有三个属性:个数 \(s_i\) 重量 \(v_i\) 和价值 \(w_i\) 现在求出在背包容量是 \(k\) 的情况可以收获的最大价值
\(n\le 10^7\)
题解
通过01背包的状态,我们很容易写出这个问题一般的转移方程
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w,dp[i-1][j-2*v]+2*w,.....,dp[i-1][j-s*v]+s*w)
滚动数组优化下变成
dp[j]=max(dp[j],dp[j-v]+w,dp[j-2*v]+2*w,.....,dp[j-s*v]+s*w)
由此我们发现 \(j\) 只与与 \(v\) 同余的位置有关,因此我们可以将同余的视作一组,那么这样一共有 \(k\) 组,这样我们的转移方程就可以写成下面这样
dp[j]=dp[j]
dp[j+v]=max(dp[j]+w,dp[j+v])
dp[j+2v]=max(dp[j]+2w,dp[j+v]+w,dp[j+2v])
dp[j+3v]=max(dp[j]+3w,dp[j+v]+2w,dp[j+2v]+w,dp[j+3v])
.....
对它进行一个变形
dp[j]=dp[j]
dp[j+v]=max(dp[j],dp[j+v]-w)+w
dp[j+2v]=max(dp[j],dp[j+v]-w,dp[j+2v]-2w)+2w
dp[j+3v]=max(dp[j],dp[j+v]-w,dp[j+2v]-2w,dp[j+3v]-3w)+3w
......
这样变形的意义在于,我们将原先不规则的、难以维护的东西转换成了规则的,好维护的东西。换句话说,我们要知道 \(j+k\times v\) 的答案,我们只需要把 \(\text{dp}[j+k\times v]-k\times w\) 和前 \(k-1\) 个相同格式的东西进行比较,这样我们就可以使用单调队列来维护最大值了
时间复杂度 \(\mathcal O(nk)\)
P2254 [NOI2005] 瑰丽华尔兹
题目
给定一个 \(n\times m\) 大小的矩阵,起点是 \((x,y)\) 。给你 \(k\) 段时间,对于第 \(i\) 段时间 \([s_i,t_i]\) ,在该时间中你可以以 \(1 \text{单位}/s\) 的速度在 \(d_i\) 方向上运动,不能走到障碍物上,问能够运动的最远距离是多少
\(n,m\le 200,k\le 200\)
题解
设 \(f[i][x][y]\) 表示考虑到第 \(i\) 段时间,结束后走到 \(x,y\) 这个格子的最远距离
直接暴力转移的复杂度是 \(\mathcal O(knm(n+m))\) 的,我们无法接受
思考一下这个东西是怎么转移的,我们发现这个转移是关于最值的,而且只关系到某一行/列,是线性的,这时候就不妨向单调队列的方向想一想
注意到对于当前时间段 \(i\) ,假设它的长度是 \(\text{len}\) ,那么能够转移到点 \((x,y)\) 上的状态实际就处在一个长度为 \(\text{len}\) 的窗口中
所以我们就可以使用单调队列优化干掉一维,时间复杂度 \(\mathcal O(knm)\)
P2216 [HAOI2007]理想的正方形
题目
有一个 \(a \times b\) 的整数组成的矩阵,现请你从中找出一个 \(n \times n\) 的正方形区域,使得该区域所有数中的最大值和最小值的差最小。
\(2 \le a,b \le 1000,n \le a,n \le b,n \le 100\)。
题解
利用单调队列,将原来 \(a\times b\) 的矩阵中的数,先将行上的最大值最小值按照 \(n\) 个一组压到一个 \((a-n+1)\times b\) 的矩阵中,然后在将压完后的矩阵中的数再拿出来,按列压到一个 \((a-n+1)\times (b-n+1)\) 的矩阵中
时间复杂度 \(\mathcal O(ab)\)
P2569 [SCOI2010]股票交易 (单调队列建模)
题目
你初始时有无限的钱,并且每天持有的股票不超过 \(\text{Maxp}\) 。 有 \(T\) 天,你知道每一天的买入价格( \(AP[i]\) ),卖出价格( \(Bp[i]\) ), 买入数量限制( \(AS[i]\) ),卖出数量限制( \(BS[i]\) )。 并且两次交易之间必须间隔 \(W\) 天。 现在问你 \(T\) 天结束后,最大收益是多少
\(1\leq BP_i\leq AP_i\leq 1000,1\leq AS_i,BS_i\leq\text{MaxP},T\le 2000,\text{MaxP}\le 2000\)
题解
设 \(f[i][j]\) 表示前 \(i\) 天之后,剩下 \(j\) 股股票的最大收益
状态转移方程如下
买入:
卖出:
复杂度是 \(\mathcal O(T\text{Maxp}^2)\) ,无法通过本题,考虑优化
同样的套路,将具有相同变量的东西放在一起
我们将 \(j\times bp[i]\) 提到括号外边去,内部就变成了一个滑动窗口,直接单调队列优化即可
时间复杂度 \(\mathcal O(T\text{Maxp})\)
posted on 2023-07-24 19:58 star_road_xyz 阅读(40) 评论(0) 编辑 收藏 举报