标签:
在写斜率优化之前,我们来回顾一下单调队列优化的dp
1. 对于如下形式的dp方程
2.对于如下形式的dp方程
但是对于形如
可以发现,若满足
令grad(i, j)表示
所以这种情况时,我们可以直接把j点删除,最后能够转移的点集只会存在这种图形,
所以最后我们维护一个上凸集即可。
但是此时我们还是没有解决最终问题,如何才能找到转移到i点的最优的点呢。可以发现最后的点集一定是一个凸集,也就是斜率单调!!这样对于k < j, grad(j,k) < f(i),时更优,从图形特点我们可以发现如果j比k优,那么j点比所有比k小的点都优,所以对于每一个f(i),我们维护一个所有比i点小的凸集,二分查找斜率比f(i)小的编号最大的点,就是最优的转移点。如果f(i)也满足单调性,比如这道题,我们还可以直接维护一个单调队列就能解决这个问题。
对于f(i)单调的这种情况,除了使用单调队列优化的斜率优化做,我们还有另外一种分治的做法,但是复杂度会变成O(nlogn) 比O(n)差。
当f(i)单调的时候,我们可以发现若a > b,则f(a) > f(b),设转移到a的最优点是c,转移到b的最优点是d,一定有c > d。也就是转移到a的最优点一定大于等于转移到b的最优点。考虑这样的分治
void dfs(int l, int r, int dl, int dr) {
//[l,r]表示现在更新[l,r]区间dp[i]的最优值
//用j -> f(i),表示j是更新f(i)最优值的最优点
//那么[dl,dr]表示更新dp([l,r])的点,一定在[dl,dr]范围内
int mid = (l + r) >> 1;
int dm = dl;
int g = inf;
for (int i = dl; i <= dr; i++) {
if(g < dp[i] + f(i, mid)) {
g = dp[i] + f(i, mid);//记录更新dp[mid]的最优
dm = i;//记录更新dp[mid]的最优点
}
}
dp[mid] = g; //更新dp[mid]的值
//因为上文叙述的单调性,
//更新[l,mid-1]的最优点,一定在[dl,dm]范围内
if(l < mid) dfs(l, mid - 1, dl, dm);
//更新[mid+1,r]的最优点,一定在[dm,dr]范围内
if(mid < r) dfs(mid + 1, r, dm, dr);
}
可以发现这个分治比起斜率优化,不仅写起来方便很多,并且适用的范围也更广。这个做法不局限于斜率单调,可以发现只要满足c是更新f(a)的最优点,d是更新f(b)的最优点,若a > b 一定有 c > d,则可以有这个分治做。
这个做法是我在codeforces 674E,跟Claris神犇的代码学会的solution,在此特地感谢Claris.这个做法着实是非常的劲啊!多一个log,但是换来编码复杂度和通用性更广的解法。
标签:
原文地址:http://blog.csdn.net/lxc779760807/article/details/51366552