• 斜率优化学习笔记


                  斜率优化

    适用范围:

    斜率优化适用于dp状态较容易维护且决策点与全局直接无关的dp

    例如:

    f[i]=min(f[k]+a[k]*a[i]);

    这里含有a[k]*a[i]这一项,所以不能简单用单调队列根据决策点的权值来判断,要使用斜率优化

    使用:

    举出一个方程式:

    f[i]=min(f[k]+(sum[i]-sum[k])^2+c);

    化简得:

    f[i]=min(f[k]+sum[i]^2+sum[k]^2-2*sum[i]*sum[k]+c)

    将min函数去掉,把含只含k的项移到等式左边,含i,k的项移到等号右边,只含i的项和常数移到前者右边,得到:

    f[k]+sum[k]^2=2*sum[i]*sum[k]-sum[i]^2-c+f[i]

    将此式看作一次函数:Y=KX+B

    Y=f[k]+sum[k]^2
    K=2*sum[i];
    X=sum[k]
    B=-sum[i]^2-c+f[i]
    long long X(long long k) {
        return ...
    }
    long long Y(long long k) {
        return ...
    }(声明函数名)

    相当于Y,K,X已知权值,我们只需找到一个最合适的X使B最小就行了

    那么如何优化?

    发现三个点k1,k2,k3

    如果连结k1,k2直线的斜率比连结k2,k3直线的斜率大,那么k2必定是无用点,去除k2

    这样一来,斜率必定是单调上升的,可以通用单调队列来维护这些点

    long long check2(long long x, long long y, long long z) {
        return (Y(y) - Y(x)) * (X(z) - X(y)) > (X(y) - X(x)) * (Y(z) - Y(y));
    }

    针对所有函数Y=KX+B分3种情况讨论

    1:K,X都有单调性

    用单调队列维护,用每个i对应的K来更新队头,再取出取出队头作为决策点,再踢无用队尾,加入i点,时间复杂度O(n)

    long long check1(long long x, long long y, long long k) {
        return (Y(y) - Y(x)) < (X(y) - X(x)) * k;
    }
        long long head = 1,
        tail = 0;
        t[++tail] = 0;
        for (long long i = 1; i <= n; i++) {
            long long K = ...(i所对应的斜率)
            while (head < tail && check1(t[head], t[head + 1], K)) head++;
            f[i] = f[t[head]] + (sum[i] - sum[t[head]]) * (sum[i] - sum[t[head]]) + c;
            while (head < tail && check2(t[tail - 1], t[tail], i)) tail--;
            t[++tail] = i;
        }

    2:K无单调性,X有单调性,用二分找出最优点,无需踢队头,其余步骤一样,时间复杂度O(nlogn)

    long long find(long long k) {
        long long l = head,
        r = tail;
        while (l < r) {
            long long mid = (l + r) >> 1;
            if (check1(t[mid], t[mid + 1], k)) l = mid + 1;
            else r = mid;
        }
        return t[l];
    }
     t[++tail] = 0;
        for (long long i = 1; i <= n; i++) {
            long long K = sumt[i];
            long long pre = find(K);
            f[i] = f[pre] + (sumc[n] - sumc[pre]) * (s + sumt[i] - sumt[pre]);
            while (head < tail && check2(t[tail - 1], t[tail], i)) tail--;
            t[++tail] = i;
        }

    3:x没有单调性,这样必须动态差点,需要用splay,坑以后再填

    例题:

    [SDOI2012]任务安排

    #include < bits / stdc++.h > using namespace std;
    const long long N = 3e5 + 10;
    long long t[N],
    c[N],
    sumt[N],
    sumc[N],
    f[N];
    long long n,
    s;
    long long head = 1,
    tail = 0;
    long long X(long long k) {
        return sumc[k];
    }
    long long Y(long long k) {
        return f[k] - sumc[n] * sumt[k] - sumc[k] * s + sumc[k] * sumt[k];
    }
    long long check1(long long x, long long y, long long k) {
        return (Y(y) - Y(x)) <= (X(y) - X(x)) * k;
    }
    long long check2(long long x, long long y, long long z) {
        return (Y(y) - Y(x)) * (X(z) - X(y)) >= (X(y) - X(x)) * (Y(z) - Y(y));
    }
    long long find(long long k) {
        long long l = head,
        r = tail;
        while (l < r) {
            long long mid = (l + r) >> 1;
            if (check1(t[mid], t[mid + 1], k)) l = mid + 1;
            else r = mid;
        }
        return t[l];
    }
    int main() {
        memset(f, 0x3f, sizeof(f));
        f[0] = 0;
        scanf("%lld%lld", &n, &s);
        for (long long i = 1; i <= n; i++) scanf("%lld%lld", &t[i], &c[i]);
        for (long long i = 1; i <= n; i++) sumt[i] = sumt[i - 1] + t[i];
        for (long long i = 1; i <= n; i++) sumc[i] = sumc[i - 1] + c[i];
        t[++tail] = 0;
        for (long long i = 1; i <= n; i++) {
            long long K = sumt[i];
            long long pre = find(K);
            f[i] = f[pre] + (sumc[n] - sumc[pre]) * (s + sumt[i] - sumt[pre]);
            while (head < tail && check2(t[tail - 1], t[tail], i)) tail--;
            t[++tail] = i;
        }
        printf("%lld
    ", f[n]);
    }

    总结:

    针对斜率优化的dp,只要将方程化简后找出k,x的单调性分情况讨论套模板即可

     

     

      

  • 相关阅读:
    npm
    React
    php区分new static 和new self
    tiny java web server
    算法可视化
    在线markdown编辑器
    JAVA
    linux find命令
    自定义windows新建菜单
    floyd算法
  • 原文地址:https://www.cnblogs.com/cwjr/p/14225335.html
Copyright © 2020-2023  润新知