zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    检测浏览器是否支持DOM2级规定的HTML事件
    click冒泡到body
    浏览器事件归类
    兼容主流浏览器的事件处理程序
    eventPhase三个状态测试
    鼠标滚轮事件(mousewheel)
    自定义右键菜单(contextmenu)
    数据库范式
    敏捷开发模式
    没有清晰的职业规划,跳槽会很失败
  • 原文地址:https://www.cnblogs.com/boson-is-god/p/6410379.html
Copyright © 2011-2022 走看看