您的位置:首页 > 其它

斜率优化+单调队列优化DP

2016-09-07 16:11 239 查看
转载自:http://blog.sina.com.cn/s/blog_508dd1e60100tvk0.html

   今天做了一道DP的题目,题意如下:
【题目大意】
有N个数,现要将它们分成连续的若干段,每段的代价为(∑Ci)^2+M,求最小的代价。
【题目分析】
容易得到这样的一个动态规划算法:
令dp[i]表示前i个数分成若干段的最小代价,能得到一个经典的动态转移方程: 
dp[i]= Min  (dp[j] +Cost(j+1,i))+M,0<=j<i。 
其中Cost(a,b)表示把a..b这些数分成一段的费用。
这个算法的时间复杂度是O(N^2)。 这题N有500000,必须优化。
我们看到Cost(j+1,i)。如果预处理出1..i的和,记为s[i],那么Cost可以表示为(s[i]-s[j])^2。
将它展开并且代入转移方程中可以得到:dp[i]= Min (dp[j] + s[i]^2 -2s[i]*s[j]+s[j]^2)+M。
因为s[i]^2是只和i有关的,可以移到Min的外面,得到
dp[i]= Min(dp[j] -2*s[i]*s[j] +s[j]^2) + M + s[i]^2。
现在我们单独考虑Min()里面的内容。
如果我们设k=2*s[i],x=s[j],y=dp[j]+s[j]^2的话(注意将和i有关的量以及和j有关的量结合到一起)
再令G=dp[i],那么将得到:
G=-k*x+y。
移项得:
y=k*x+G。
看到这个式子,知道斜率优化的朋友显然可以做出来了。
这里我讲讲何谓斜率优化。
得到这个式子之后,我们可以看到,k是一个常数(由当前枚举的i在O(1)时间内计算得出)。将这个式子看成是一个直线的函数表达式的话,k就是斜率,也就是说这是一个斜率固定的直线。
y和x则是和j有关的常量。而j的这些值应该都已经在之前计算过了。(因为j<i)
这个式子中,G是未知的,G和y以及x有关。
如果我们把每个j对应的x和y值看成一个坐标系中的点的话。
那么当我们枚举到i时,坐标系中就有一系列的点。
对于每个点,做一条斜率为k的直线,就能得到一个G的值。G的值为这条直线与y轴交点的纵坐标。
我们可以看到,实际上,如果我们让G的值由负无穷变化到正无穷,相当于一条直线,它满足斜率为k,然后从坐标系的下方慢慢地向上平移到坐标系的上方。
那么,我们要找到,G的最小值,就是在这个过程中,这条直线所碰到的第一个点!
而这个点,必然是这个点集的凸包上的点。(不知道凸包概念的去baidu一下好了)
再加上很关键的一点。随着i的增加,点的坐标是单调不下降的(s[i]),直线的斜率也是单调不降的(2s[i])!
满足这两个单调性,我们就可以利用单调队列,来维护一个凸壳。因为我们要找G的最小值,所以要维护一个下凸壳。方法和之前那篇数形结合题一样,类似Garham求凸包的算法。
之所以可以用单调队列是因为坐标和斜率都满足单调性的话,可以证明每个点如果不是某个i的最优决策,也不能会是之后的i的最优决策,可以被抛弃。
因为每个i只会进出队列一次,所以时间复杂度降为O(N),只需要保存单列就可以,空间复杂度为O(N),比较完美地解决了这个问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: