其他分享
首页 > 其他分享> > DP优化

DP优化

作者:互联网

一、单调队列优化

很简单,对于 \(dp_i=\min (A_j+B_i)\) 的状态转移方程,可以丢进单调队列,时间复杂度 \(O(n)\)。

二、斜率优化

如果是对于 \(dp_i=\min(A_i\cdot B_j+C_i+D_j)\) 的状态转移方程,就单调队列不了了,因为有一项同时存在 \(i\) 和 \(j\)。

我们考虑把不和 \(j\) 有关的都丢到等式右边,那左边要么就只和 \(j\) 有关,要么和 \(i\)、\(j\) 都有关。

考虑这样的一次函数 \(y=k\cdot x+b\),稍微移项可得 \(b=y-k\cdot x\),所以我们把只跟 \(i\) 有关的都看做 \(b\),只跟 \(j\) 有关的看做 \(y\),同时跟 \(i\) 和 \(j\) 有关的看做 \(k\cdot x\)。

对于上面那个式子,我们先写成 \(f_i-C_i=\min(A_i\cdot B_j+D_j)\),所以 \(b=dp_i-C_i\),\(x=B_j\),\(y=D_j\),\(k=A_i\)。因为 \(C_i\) 是定值,所以使 \(dp_i\) 最小即使 \(b\) 最小,图像如下:

image

也就是你每次确定一个 \(k_i\) 以后,需要寻找一个过某个点的该斜率的线,使得截距 \(b\) 最小,观察图像,这条线必然过是两条斜率分别小于 \(k_i\) 和大于 \(k_i\) 的直线的交点,用个单调队列维护这个凸包即可。

具体维护方法:

设第 \(i\) 个点和第 \(j\) 个点连接的直线方程斜率为 \(k(i,j)\)。

1、对于队首元素,由于维护的是一个凸包,所以 \(k\) 随着 \(i\) 的增加是单调的,把 \(k(q_j,q_{j+1})<k_i\) 的 \(j\) 都弹出;

2、这条直线最先碰到的必然是最下面那个点,即队首的点,用队首转移即可;

3、对于队尾元素,同样的,因为维护的是一个凸包,所以凹进去的部分要弹出去,即弹出所有 \(k(q_{j-1},q_j)>k(q_{j},q{j+1})\) 的 \(j\) 即可。

e.g. [HNOI2008] 玩具装箱

三、WQS二分

对于有限制的 \(dp\),比如 [Luogu P4983] 忘情

状态转移方程很容易得到,即 \(dp_i=\min(dp_j+(s_i-s_j+1)^2)\),\(s\) 表示 \(a\) 数组的前缀和,很显然如果没有只能分 \(m\) 段的限制,这就是个斜率优化的简单题,然而这里有一个限制,我们画出分成的段数与 \(ans\) 的图像,很显然在 \(m\) 取到最值时,这个图像是个凸的(否则你可以随便合并两个嘛),并且最高点得是 \(m\),考虑画图分析。

image

我们画一些斜率为 \(k\) 的线,观察每一条线 \(y=k\cdot x+b\),移项得 \(b=y-k\cdot x\),那么我们要使得 \(x=m\) 时截距 \(b\) 最大,然后我们惊奇地发现,\(b_i\) 只是 \(y_i\) 即 \(dp_i\) 减去了 \(x\) 个 \(k\),也就是把转移式改成 \(dp_i=\min(dp_j+(s_i-s_j+1)^2)-k\) 即可。

我们二分 \(k\),便可以得到答案。

标签:队列,min,cdot,斜率,DP,单调,优化,dp
来源: https://www.cnblogs.com/vegtable-foraino/p/16286263.html