【学习记录】斜率优化

记录一下斜率优化算法。

斜率优化,顾名思义就是用数学中函数常用的斜率进行优化。这里拿 DP 来举例说明。

斜率

我们任取两个决策点 \(j_1\)\(j_2\) \((j_1<j_2)\)。我们不妨先设决策点 \(j_2\) 不劣于决策点 \(j_1\)。假如说我们的状态转移方程为

\[dp_i=\min_{j<i}dp_j+w\left(j+1,i\right), \]

同时,设

\[w\left(j+1,i\right)=aij+bi+cj+d. \]

\(a,b,c,d\) 均为常数)

那么显然,这个决策优异性表现为越小越优(因为 \(\min\) 函数)。所以,在任意某个 \(i\) 处,一旦我们最先的关于决策点优秀度的假设成立,那么对于两个决策点,就满足

\[dp_{j_1}+w\left(j_1+1,i\right)\leq dp_{j_2}+w\left(j_2+1,i\right) \]

的关系。展开,得

\[dp_{j_1}+aij_1+bi+cj_1+d\leq dp_{j_2}+aij_2+bi+cj_2+d. \]

去掉两边的相同项,得

\[dp_{j_1}+aij_1+cj_1\leq dp_{j_2}+aij_2+cj_2. \]

这时,如果我们变一变形式:

\[\begin{aligned} dp_{j_1}+aij_1+cj_1&\leq dp_{j_2}+aij_2+cj_2,\\ ai(j_2-j_1)&\geq dp_{j_1}-dp_{j_2}+cj_1-cj_2,\\ ai&\geq \frac{-\left(\left(dp_{j_2}+cj_2\right)-\left(dp_{j_1}+cj_1\right)\right)}{j_2-j_1}. \end{aligned} \]

不难发现,此时这个不等式被我们化成了部分形式相仿的一些部分。不妨归纳一下。可设

\[Z=ai,\quad Y(j)=-(dp_j+c_j),\quad X(j)=j. \]

于是,原不等式就可化为

\[Z \geq \frac{Y(j_2)-Y(j_1)}{X(j_2)-X(j_1)}. \]

(其中,\(Z=ai\)\(i\) 阶段定值)

定睛细看,这不就是斜率吗?

优化

既然斜率有了,问题就解决了一半,甚至不止一半。那么,优化当然就是迫在眉睫的事情。

注意到刚刚得出的关于斜率的不等式

\[Z \geq \frac{Y(j_2)-Y(j_1)}{X(j_2)-X(j_1)} = k. \]

倘若我们在平面直角坐标系 \(XOY\) 上任取 \(3\) 个点 \(j_1,j_2,j_3\),不妨设 \(X(j_1)<X(j_2)<X(j_3),k_1>k_2\)。满足坐标 \(\left(X(j),Y(j)\right)\),且 \(j_1,j_2\) 点斜率为 \(k_1\)\(j_2,j_3\) 点斜率为 \(k_2\)

我们就有 \(3\) 种情况,分别是

\[\begin{aligned} k_2 &\leq Z <k_1,\\ k_2 &< k_1 \leq Z,\\ Z &< k_2 < k_1. \end{aligned} \]

而假设相对优秀情况用 \(v(j)\) 表示,那么以上 \(3\) 种情况对应的比较应该是

\[\begin{aligned} &v(j_1),v(j_3)>v(j_2),\\ &v(j_3)>v(j_2)>v(j_1) ,\\ &v(j_1)>v(j_2)>v(j_3). \end{aligned} \]

还记得我们的目的吗?我们的目的是找到所有决策点 \(j\) 中最优的,也就是说,次优的或更劣的一定不会被我们选中。观察上述决策点规律,不难发现 \(j_2\) 决策点永远都不是最优的,最高只可能是次优情况。回顾前提条件,我们得到了这样一个结论:

当存在 \(3\) 个决策点 \(j_1,j_2,j_3(X(j_1)<X(j_2)<X(j_3))\) 时,若满足 \(k_1>k_2\),则最优决策点只能位于 \(j_1,j_3\)

也就是说,在真实代码操作过程中,我们可以动态维护 \(3\) 个点,如果斜率出现 \(k_1>k_2\) 的情况,就可以删点。考虑全面、极端的情况。假如已经将所有的中项点(即 \(j_2\))删除完毕,并且创建了一个新的 \(j_1,j_3\) 点连接,得到了新的斜率 \(k_3\)(当然,如果发现 \(j_1\)\(j_3\) 不合法,\(k_3\) 也会被其他斜率代替)。如果 \(k\) 在实数中较全面分布,那么 \(k\) 将一直从 \(-\infty,\dots,p,(0,)\dots ,+\infty\)。在图象上,\(j\) 点将先严格下降,而后严格上升,且严格下降坡度越来越缓,而严格上升坡度越来越陡。不难想象出,这是一个类似于碗状的图形。我们称它为下凸包

但是,请始终记住,这是查找 \(\min\)。如果查找 \(\max\) 呢?那我们不等式中的 \(\geq\) 就会变成 \(\leq\)。所以,恰好相反,最终的图形是上凸包。

进一步优化

不论是哪个方向的凸包,最优节点都会产生在构成凸包的这些点中。如果想要进一步优化,我们可以给这些凸包上的点再缩圈。这些就要看题目具体本身的东西了。拿题目 \(\text{P3195 [HNOI2008] 玩具装箱}\) 来举例。这道题,我们会发现其在决策过程中满足四边形不等式,换句话说,具有一定的单调性,这样我们只要往一个地方不断推进就行了。在这里,我们采用维护一个双端队列的方式,记录队头为 \(l\),队尾为 \(r\)。对于每个 \(i\) 决策就变成了:

  • 从队头 \(l\) 开始,对于队列中每个点 \(Q_j(j\geq l)\),如果该点满足 \(k_1>k_2\),那么就向这个点推进。因为其满足一定的单调性。如果推进不了了,那么 \(Q_{l,l+1,\dots,j-1}\) 就一定劣于 \(Q_j\),那么 \(l \gets j\),停止推进。
  • 更新 \(dp_i\)
  • 现在,由于加入了一个新点 \(q\),部分队列中的斜率将不再适配目前新点要求。也就是说,加入了新点之后,可能会出现 \(k_1<k_2\) 的情况。这个时候,我们就要继续删点。对于 \(3\) 个元素 \(Q_{r-1},Q_{r},q\),如果说 \(k(Q_{r-1},Q_{r})\geq k(Q_{r},q)\)(这里将斜率相同去重),那么 \(r \gets r-1\);如果恰恰相反,说明此时不需要再进行删点了。直接 \(r \gets r+1\),然后 \(Q_{r} \gets q\)

那么,这个问题就解决了。

(贴一下代码)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,L;
int c[50010];
int s[50010];
int l=1,r=1;
int q[50010];
int dp[50010];
int X(int j){
	return s[j];
}
int Y(int j){
	return (s[j]+L)*(s[j]+L)+dp[j];
}
inline long double slope(int u,int v){
	return (long double)(Y(v)-Y(u))/(X(v)-X(u));
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin>>n>>L;
	L++;
	for(int i=1;i<=n;i++){
		cin>>c[i];
		s[i]=c[i]+1+s[i-1];
	}
	for(int i=1;i<=n;i++){
		while(l<r&&slope(q[l],q[l+1])<=2*s[i]){
			l++;
		}
		dp[i]=dp[q[l]]+(s[i]-s[q[l]]-L)*(s[i]-s[q[l]]-L);
		while(l<r&&slope(q[r-1],q[r])>=slope(q[r],i)) r--;
		q[++r]=i;
	}
	cout<<dp[n]<<endl;
	return 0;
}

总结

无论是什么优化,一定都是从最基本的开始推的。如果没有什么优化的思路,不如假设一下,从已知条件(或假设的条件)出发,看看有没有什么特别的性质,看看有没有什么熟悉的形式(例如斜率式),看看能不能把抽象的东西具象化,看看能不能采取大脑更加认同的方式(图形化处理)。也许这样,说不定就给我们的思考做了一次完美的剪枝。

(点个赞再走呗)

posted @ 2025-08-17 12:04  WegestGao  阅读(17)  评论(0)    收藏  举报