「Note」DP 方向 - DP 优化
1. 单调队列优化 DP
1.1. 简介
当一个选手比你小还比你强,你就打不过他了。这是对单调队列简单形象的概括。
单调队列在转移的过程中不断排除不可能成为决策点的元素,使每次转移寻找决策点的时间复杂度降为 \(O(1)\)。一般地,可被单调队列优化的转移式可被写为如下形式:
其中需要满足 \(l_i\) 单调不降,\(A_i\) 是只与 \(i\) 有关的变量,\(B_j\) 是只与 \(j\) 有关的变量。
不难发现单调队列要维护的是 \(F_i+A_i\),每次 \(j1\) 入队时,若满足 \(F_{j1}+A_{j1}>F_{j0}+A_{j0}\) 则将 \(j0\) 弹出,直到不满足条件或者队列为空。
此时取队头元素,它一定满足在范围内最优,将其作为决策点即可。特殊地,有时需要特判队列为空的情况。
1.2. 常见技巧
大部分情况下,转移方程并不显著,因为在其中的贡献往往与 \(i,j\) 都有关,此时尝试将贡献转化为 \(A_j+B_i\) 的形式,然后在单调队列中维护 \(F_i+A_i\) 即可。
1.3. 例题
\(\color{limegreen}{P1776}\)(单调队列优化多重背包)
\(v_i\) | \(w_i\) | \(c_i\) | \(F_{i,j}\) |
---|---|---|---|
物品 \(i\) 的体积 | 物品 \(i\) 的价值 | 物品 \(i\) 的个数 | 前 \(i\) 个物品放入总体积为 \(j\) 的物品的最大价值 |
显著地,有以下转移方程:
设 \(j'=j-k\times v_i\),在保证 \(i\) 不变的情况下,分别考虑每个 \(j'\) 对 \(j\) 的贡献,此时有 \(k=\frac{j-j'}{v_i}\)。此时 \(j,j'\) 在 \(\bmod v_i\) 的意义下同余,设 \(j=t\times v_i+r\) 其中 \(0\le r<v_i\),考虑将贡献拆分有 \(j'=j-k\times v_i,k=\left\lfloor\frac{j}{v_i}\right\rfloor-\left\lfloor\frac{j'}{v_i}\right\rfloor=t-\left\lfloor\frac{j'}{v_i}\right\rfloor\),将原方程改写:
不难想到在枚举 \(r\) 的基础上,枚举 \(j\) 进行单调队列优化。
$\text{Code}$:
#define LL long long
#define UN unsigned
#include<bits/stdc++.h>
using namespace std;
//--------------------//
const int N=1e5+1;
int n,V;
int v[N],w[N],c[N];
int ans,f[N];
int head,tail;
int q[N][2];
//--------------------//
int main()
{
scanf("%d%d",&n,&V);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&w[i],&v[i],&c[i]);
for(int i=1;i<=n;i++)
for(int r=0;r<v[i];r++)
{
head=tail=1;
q[1][0]=r,q[1][1]=f[r];
for(int tem,t,j=r+v[i];j<=V;j+=v[i])
{
tem=f[j]-j/v[i]*w[i],t=j/v[i];
while(head<=tail&&q[head][0]<j-c[i]*v[i])
head++;
f[j]=max(f[j],q[head][1]+t*w[i]);
while(head<=tail&&q[tail][1]<tem)
tail--;
q[++tail][0]=j,q[tail][1]=tem;
}
}
for(int i=1;i<=V;i++)
ans=max(ans,f[i]);
printf("%d",ans);
return 0;
}
\(\color{royalblue}{P3089}\)
\(x_i\) | \(p_i\) | \(F_{i,j}\) | \(now_i\) |
---|---|---|---|
目标点 \(i\) 的坐标 | 目标点 \(i\) 的分数 | 跳至目标点 \(i\),上一个目标点是 \(j\) ,所能获得的最大分数 | 当前单调队列还未加入的最近位置 |
先考虑单方向的做法,显著地,按照 \(x_i\) 排序后有以下转移方程:
我们发现当 \(i\) 固定时并不能直接用单调队列优化,因为维护的 \(\max\) 中存在两个不固定值 \(j,k\)。考虑固定 \(j\),当 \(i\) 上升时,\(k\) 的范围单调不减,此时可以用单调队列维护。
对于每一个 \(j\) 维护一个单调队列,用 \(now_j\) 记录还未进队的最近位置,每次枚举 \(i\) 时,将符合条件的 \(k\) 入队,并更新 \(now_j\)。
至于向另一方向跳的情况,将数轴左右翻转再做一遍 DP 即可。
\(\color{royalblue}{P4544}\)
\(ka\) | \(e\) | \(x_i\) | \(c_i\) | \(v_i\) | \(f_{i,j}\) |
---|---|---|---|---|---|
需要饲料总数 | 中点坐标 | 商店 \(i\) 坐标 | 商店 \(i\) 库存 | 商店 \(i\) 价格 | 走到商店 \(i\) 经过交易后得到 \(j\) 个饲料的最小消费 |
先按照 \(x_i\) 排序,显著地,转移方程:
将其转化:
显著的单调队列。其中需要注意的是初值的设置以及转移的始末位置。
x. 前置知识 决策单调性
x.1. 简介
决策单调性是一个优秀的性质,对于具有决策单调性的动态规划可以采用很多方法进行优化。
通常地,决策单调性体现在 1D 维度上。当 \(j<j'<i<i'\) 时,若 \(F_i\) 从 \(F_{j'}\) 转移过来,那么 \(F_i'\) 不可能从 \(F_j\) 转移过来,只可能从 \(F_j'\) 或之后的状态转移。这种性质可称之为决策单调性。
x.2. 四边形不等式
四边形不等式如下,在 \(a<b<c<d\) 的情况下 \(w(a,d)+w(b,c)>w(a,c)+w(b,d)\)。
设 \(F_i\) 由 \(F_j+w(i,j)\) 转移来,若满足四边形不等式则称其具有决策单调性。
证明略,待施工。
2. 斜率优化
2.1. 简介
当一个最优化 DP 的转移方程形如:
即其中有一些只关于 \(i,j\) 其中一个的项和一个关于 \(i,j\) 两个变量的项,可以考虑用斜率优化来解决。
先对原式进行化简。设 \(F_i\) 由 \(F_j\) 转移过来,有 \(F_i=F_j+A_i+B_j+a_i\times b_j\),移项得:
设 \(y=F_j+B_j,x=b_j,k=a_i,b=A_i-F_i\),当 \(i\) 固定时,我们得到了一个 \(k\) 固定的一次函数,使其经过不同的 \((x,y)\)(与 \(j\) 有关),可以得到不同的截距 \(b\)(与 \(F_i\) 有关),其中取截距最大或最小,可以得到 \(F_i\) 的最大或最小值,因题而异。
具体地,我们维护一个凸包,取一次函数与切点为决策点。