【斜率优化DP】
我们知道, 有些DP方程可以转化成DP[i]=f[j]+x[i]的形式,其中f[j]中保存了只与j相关的量。这样的DP方程我们可以用单调队列进行优化,从而使得O(n^2)的复杂度降到O(n)。 可是并不是所有的方程都可以转化成上面的形式,比如dp[i]=dp[j]+(x[i]-x[j])*(x[i]-x[j])。如果把右边的乘法化开的话,会得到x[i]*x[j]的项。这就没办法使得f[j]里只存在于j相关的量了。于是上面的单调队列优化方法就不好使了,所以我们需要一种新的优化方法,叫做 斜率优化。 拿本题来说,我们设dp[i]表示装到第i个玩具的时候最少的花费,sum[i]表示前i个玩具的长度和。于是方程就是: dp[i] = min{ dp[j] + [i+sum[i]-(j+1+sum[j])-L] 2 }; 题目的N<=50000,二维铁定超时了。我们就来看看斜率优化如何做到从O(n^2)复杂度降到O(n)。 分析: 我们假设k<j<i。如果在j的时候决策要比在k的时候决策好,那么也是就是dp[j] + [i+sum[i]-(j+1+sum[j])-L] 2 < dp[k] + [i+sum[i]-(j+1+sum[k])-L] 2。(因为是最小花费,所以优就是小于) 两边移项一下得到:[dp[j]+(j+1+sum[j])^2-(dp[k]+(k+1+sum[k])^2)]/(2*( (j+1+sum[j])-(k+1+sum[k]) )) < (i + sum[i]-L)。我们把dp[j]-num[j]^2看做是yj,把2*num[j]看成是xj。 那么不就是(yj-yk)/(xj-xk) < A[i]么? 左边是不是斜率的表示? 那么(yj-yk)/(xj-xk) < A[i]说明了什么呢? 我们前面是假设j的决策比k的决策要好才得到这个表示的,那么 g[j,k]=(yj-yk)/(xj-xk) < A[i]代表这j的决策比k的决策要更优。 斜率优化在于: ①设k<j<i,如果g[i,j] < g[j,k],那么j点便永远不可能成为最优解 ,可以直接将它踢出我们的最优解集。‘ 为什么呢?我们假设g[i,j] < A[i],那么就是说i点要比j点优,排除j点。如果g[i,j] >= A[i],那么j点此时是比i点要更优,但是同时g[j,k]>g[i,j]>A[i]。这说明还有k点会比j点更优,同样排除j点。排除多余的点,这便是一种优化! 由于我们排除了g[i,j] < g[j,k]的情况,所以整个有效点集呈现一种下凸性质,即k j的斜率要小于j i的斜率。这样,从左到右的斜率之间就是单调递增的了,所以我们就可以对g维护一个单调队列。 同时, 函数A[i]也要具有单调递增的特性,所以第二个优化就在于: ②如果单调队列的头两个点为i, j,斜率g[j, i] < A[i],则说明j优于i,并且由于A单调递增,所以g[j,i]恒小于A[],所以可以直接把i排除掉。 于是对于这类题斜率优化做法可以总结如下: 1,用一个单调队列来维护解集。 2,假设队列中从头到尾已经有元素a b c。那么当d要入队的时候,我们维护队列的下凸性质,即如果g[d,c]<g[c language=",b"][/c],那么就将c点删除。直到找到g[d,x]>=g[x,y]为止,并将d点加入在该位置中。 3,求解时候,从队头开始,如果已有元素a b c,当i点要求解时,如果g[b,a]<A[i],那么说明b点比a点更优,a点可以排除,于是a出队。最后dp[i]=getDp(q[head])。