斜率优化+单调队列优化DP<转>

来源:互联网 发布:天尚网络电视机顶盒 编辑:程序博客网 时间:2024/06/09 16:34

最近发现HDU上的题目msPOJ上的好些似的, 因为每次都是一道题搞一天。 也说明我真的还是很菜啊。 昨天搞了一道题,一道最短路的题目,弄了一天。其实最主要的原因是没有搞清楚DijkstraSPFABellman_ford这几个最短路算法的复杂度。这里作个总结,数组实现的Dijkstra,复杂度为O(N^2);用优先队列优化的Dijkstra复杂度为O(ElogE),但是图需要用邻接矩阵实现;Bellman_fordO(VE)SPFA,O(KE),K<<V; HDU 3349,给出一张图,题意是可以选择一条边进行减半,其他的不能变,求从ST的最短路。直接枚举每条边,然后进行Dijkstra,由于题意中的N给了1e5N^2的算法显然会超时,分析一下可知,超时的原因是每次只枚举了一条减半的边,但是重复求了其他点的最短路,一个优化是将S到每个点的最短路保存在一个数组里,然后求出每个点到T点的最短路。依次枚举每条边,min = MIN(sDis[i] + tDis[j] + cost[i][j]/2 ),<i,j> ;求两次最短路的时候,可以用Heap+Dijkstra,当然更容易实现的自然是SPFA。 一开始没有想到用SPFA,一直过不了,调了半天,一直WA。 然后就是看到了网上有人用SPFA,换了SPFA,但是没有考虑起点和终点是同一个点的情况,各种WA。 最后msWA20+次,才AC, 汗。。。。。

 

    今天做了一道DP的题目,题意如下:

【题目大意】

N个数,现要将它们分成连续的若干段,每段的代价为(Ci)^2+M,求最小的代价。

【题目分析】

容易得到这样的一个动态规划算法:

dp[i]表示前i个数分成若干段的最小代价,能得到一个经典的动态转移方程: 

dp[i]=  Min  (dp[j] + Cost(j+1,i))+M0<=j<i 

其中Cost(a,b)表示把a..b这些数分成一段的费用。

这个算法的时间复杂度是O(N^2)。 这题N500000,必须优化。

我们看到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是一个常数(由当前枚举的iO(1)时间内计算得出)。将这个式子看成是一个直线的函数表达式的话,k就是斜率,也就是说这是一个斜率固定的直线。

yx则是和j有关的常量。而j的这些值应该都已经在之前计算过了。(因为j<i

这个式子中,G是未知的,Gy以及x有关。

如果我们把每个j对应的xy值看成一个坐标系中的点的话。

那么当我们枚举到i时,坐标系中就有一系列的点。

对于每个点,做一条斜率为k的直线,就能得到一个G的值。G的值为这条直线与y轴交点的纵坐标。

我们可以看到,实际上,如果我们让G的值由负无穷变化到正无穷,相当于一条直线,它满足斜率为k,然后从坐标系的下方慢慢地向上平移到坐标系的上方。

那么,我们要找到,G的最小值,就是在这个过程中,这条直线所碰到的第一个点!

而这个点,必然是这个点集的凸包上的点。(不知道凸包概念的去baidu一下好了)

再加上很关键的一点。随着i的增加,点的坐标是单调不下降的(s[i]),直线的斜率也是单调不降的(2s[i])

满足这两个单调性,我们就可以利用单调队列,来维护一个凸壳。因为我们要找G的最小值,所以要维护一个下凸壳。方法和之前那篇数形结合题一样,类似Garham求凸包的算法。

之所以可以用单调队列是因为坐标和斜率都满足单调性的话,可以证明每个点如果不是某个i的最优决策,也不能会是之后的i的最优决策,可以被抛弃。

因为每个i只会进出队列一次,所以时间复杂度降为O(N),只需要保存单列就可以,空间复杂度为O(N),比较完美地解决了这个问题。


原创粉丝点击