斜率优化dp小结

斜率优化小结

前言

一般来说,我们可以把斜率优化 \(dp\) 对应的可以解决的问题分成这几类:

\(x\) 单调且 \(k\) 单调,\(x\) 不单调且 \(k\) 单调,\(x\) 单调且 \(k\) 不单调,\(x,k\) 都不单调。

我们可以只在第一种情况中使用单调队列直接维护做到线性复杂度,剩下的都可以直接使用李超树维护凸包即可。

如果想深入学习一般的单调队列来处理斜率优化的问题,可以见辰星凌的博客

单调队列

这里我们只讲如何维护 \(x,k\) 均单调的情况。

直接以 P4072 [SDOI2016]征途 为例来讲解,这里直接不考虑前面的凸优化过程,只考虑斜率优化的部分。

现在设 \(dp_i\) 为前 \(i\) 条路的最优答案,\(x_i\) 为路程长度的前缀和,有转移方程:

\[\large dp_i=\min_{j=0}^i\{dp_j-2x_ix_j+x_j^2\}+x_i^2 \]

那么设现在有决策 \(j,k\) ,决策 \(j\) 优于决策 \(k\) 当且仅当有:

\[\large \begin{split} dp_j-2x_ix_j+x_j^2&<dp_k-2x_ix_k+x_k^2 \\ dp_j-dp_k+x_j^2-x_k^2&<2x_i(x_j-x_k) \\ \frac{dp_j-dp_k+x_j^2-x_k^2}{x_j-x_k}&<2x_i \\ \frac{(dp_j+x_j^2)-(dp_k+x_k^2)}{x_j-x_k}&<2x_i \\ \frac{Y(j)-Y(k)}{X(j)-X(k)}&<2x_i \end{split} \]

这就是维护队首的最优决策点。

那么我们还要在每次决策过后更新队尾的决策点:

也就是判断当前末尾三个点的斜率关系。

这道题的斜率优化部分代码如下:

inline ll X(int i){return sum[i];}//决策点的横坐标
inline ll Y(int i){return dp[i]+sum[i]*sum[i];}//决策点的纵坐标
inline double slope(int i,int j){return (Y(i)-Y(j))/(X(i)-X(j));}//求斜率
//以上是方便斜率优化的函数

//下面是斜率优化部分
sta[h=t=0]=0;//初始化队列
for(int i=1;i<=n;i++){//eps是精度误差
	while(h<t&&slope(sta[h],sta[h+1])<2.0*sum[i]+eps) h++;//队首的更新(决策时的更新)
	dp[i]=dp[sta[h]]+(sum[i]-sum[sta[h]])*(sum[i]-sum[sta[h]])+mid;//这个mid不用管,是凸优化自带的系数
	num[i]=num[sta[h]]+1;//这个是凸优化的东西,不用管
	while(h<t&&slope(sta[t-1],sta[t])>slope(i,sta[t])-eps) t--;//队尾的更新(待决策时的更新,更新待决策点)
	sta[++t]=i;//放入队列
}

李超树

其余所有的情况都可以使用李超树,因为在这些情况当中,李超树的复杂度不会更劣。

详见 李超树复习 ,更建议直接去学习一下李超树就知道了。

posted @ 2021-09-24 11:30  __Anchor  阅读(67)  评论(0)    收藏  举报