F. Bear and Bowling 4(斜率优化)

题目链接: http://codeforces.com/contest/660/problem/F

这篇博客讲的挺详细的: (下面说的都是借用链接博客中的一些 Notations)

http://blog.csdn.net/kg20006/article/details/51333093

这题真的是让我对斜率优化理解又进一步了。 

之前的斜率优化我所知道都是像  http://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html

这篇博客里说的, 但是这一题 sum[i]不是单调的, 或者说这题是斜率优化的一个推广形式

Core: dp优化就是去掉没用的点, 之前我所理解的斜率优化是维护队首的,  那是因为sum[i] 是单调的,所以对于队列中的

元素有 (k < j < i) 有 gradient(j, k) < sum[j], gradient(i, j) < sum[i], 从而有i比j优,j比k优, 因为sum[i] > sum[j] 所以如果gradient(i, j) > sum[i]

那么显然 gradient(i, j) > sum[j] 不可能加入到队列中去, 所以队列中斜率是递增的, 所以队列中的都是前一个比后一个更优, 从而我们可以维护队首是最优值。

这都是很显然的但是都是建立在sum数组是单调的情况下。

而这一题就是sum数组不是单调的,所以我们就没有办法去维护队首最优。理由是我们可能会多删掉一些点

根据队列的单调性,我们可以得出这个函数的图像为单峰函数如(y = x^2)   我就拿y = x^2这个函数来举个栗子说明为啥会可能多删掉点

设k < j < i  那么斜率是这样的 gradient(i, j) > max{gradient(i, k), gradient(j, k)} 按照之前的做法,那么就可以去掉k点了, 但是后面

有可能会出现一个点m使得 gradient(m, k) > gradient(i, j) 但是由于k已经被删除了, 从而导致了结果的错误 所以这里不能够删除

能够删除的只有http://blog.csdn.net/kg20006/article/details/51333093这篇博客里说的那种情况 

对于单峰函数可以用三分去求解最大的极值函数y(x)的值  用二分可以求解对应的下标x = argmax(f(x)) 原理就是由于斜率队列是单调的根据费马引理f'(x) = 0 对应着极值

所以gradient最接近sum[r] 的 就是最大值的取值下标

#include <iostream>
#include <string.h>
#include <algorithm>
#include <string>
#include <stdio.h>
using namespace std;

const int maxn = 2e5 + 5;
int n, head, tail; 
long long a[maxn], sum[maxn], p[maxn];
int q[maxn];

long long y(long long x) {
    return p[x] - x*sum[x];
}

long long getdp(int i, int j){
    return p[i] - p[j] - j*(sum[i] - sum[j]);
}

double getgradient(int i, int j) {
    double dy = y(i) - y(j);
    double dx = i - j; 
    return dy / dx;
}

int solve(long long x) {
    int l = head, r = tail - 1; int res = l;
    while (l <= r) {
        int m = l + r >> 1;
        if (getgradient(q[m], q[m-1]) < -x) l = m + 1, res = m;
        else r = m - 1;
    }
    return q[res];
}

int main() {
    ios::sync_with_stdio(false);
    cin >> n;  sum[0] = p[0] = 0ll; 
    long long ans = 0ll;
    for (int i = 1; i <= n; ++i) 
        cin >> a[i], sum[i] = sum[i - 1] + a[i], p[i] = p[i - 1] + (long long)i*a[i];
    head = tail = 0; q[tail++] = 0;
    for (int i = 1; i <= n; ++i) {
        int j = solve(sum[i]);
        ans = max(ans, getdp(i, j));
        while (head < tail-1 && getgradient(i, q[tail - 1]) < getgradient(q[tail - 1], q[tail - 2])) --tail;
        q[tail++] = i;
    }
    cout << ans << endl;
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/boson-is-god/p/6410379.html