前言:
也是好久没有写题解了,最近主要学习了单调栈单调队列以及斜率优化DP这几个知识点,对于较难的斜率优化DP,做个小小的总结吧。
正(che)文(dan):
T1 hdu 3507
在一个风和日丽的早上,你打开了网页,点进了hdu,偶然间看到了这道题,不屑的以为这仅仅是一个很水的DP,2分钟给出DP方程式,很快的写完后发现n的范围居然是500000,这让已经推出来的 O(n2) 复杂度的递推式情何以堪,所以就产生了一种高逼格的优化方式:斜率优化。
这道题的方程式是什么呢?
dp[i]=min(dp[j]+sum[i]2−sum[j]2+M),j∈(0,i)
我们发现从1到n计算i的时候,每一次都是将
1∼i−1
的最优决策拿出来更新
i
,所以我们可不可以搞出来一种方式来维护这个最优解,使得在i 递增的时候每次更新都能在
O(1)
的时间拿出来这个解呢。
我们先假设两个决策 k和j ,且 j>k 。
对于二者来说他们对应的值分别为:
dp[j]+(sum[i]−sum[j])2+M
dp[k]+(sum[i]−sum[k])2+M
如果这个时候
j这个决策是比k
优的,那么说明
j是完全可以取代k
的,因为
j是在k的后面出现
,随着时间的递增,可以证明
k是不可能再比j优的
,所以这个时候可以删掉
j
,但是反之就是不一定的,因为随着时间的递增j有可能更优于k ,所以不可以删掉
k
>
j优于k时
dp[j]+(sum[i]−sum[j])2+M<=dp[k]+(sum[i]−sum[k])2+M
则
dp[j]−dp[k]+sum[j]2−sum[k]22∗(sum[j]−sum[k])<=sum[i]
我们把
dp[j]−dp[k]+sum[j]2−sum[k]22∗(sum[j]−sum[k])
叫做 g[k,j]
那么就可以得到这个结论
对于决策k与j来说,k<j,如果g[k,j]<sum[i],那么就是在i这个时间点时,j优于k。
所以到i时,在给dp[i]赋值之前,需要维护这个队列的队首,如果 g[q[head],q[head+1]]<=sum[i]
q[head+1]已经比q[head]更优了,所以head就可以删掉,这样我们每次从队头拿出来的值都是最优的值
然而队尾的处理就没有那么简单了,因为两个决策的时候不好考虑删不删,所以我们引入第三个决策 l且k<j<l
如果 g[k,j]>g[j,l]
1. g[j,l]>sum[i]
则说明 j比l优 ,此时我们并不能做什么,但是同时 g[k,j]>g[j,l]>sum[i]
所以又有
k比j
优,这个时候符合已经推导出来的删点法则,我们就可以把
j
这个决策删除了
2. g[j,l]<=sum[i]
则说明
l比j
优,根据删点法则,直接可以把
j
给删除,
那么从另一个角度说,g[k,j]>g[j,l] 的时候是删除掉j的。
所以我们维护的是 g[k,j]<=g[j,l] 的情况也就是一个下凸函数(传不上图…)
这样分别去维护队首以及队尾就可以搞定这道题。
T2 玩具装箱
维护的仍然是最小值。
这道题也是挺简单的,只是在推导过程中心细一点就可以了。
我在这道题上将 sum[i] 定义为 1∼i 的求和以及间隔数,以便推导时更加简洁。
最后推导出来的斜率式子是:
dp[j]−dp[k]+(sum[j]−sum[k])∗(sum[j]+sum[k]+2∗(L+1))2∗(sum[j]−sum[k])
其他的重复上一题的过程。