斜率优化小总结

斜率优化dp 的小总结

首先下凸包/上凸包的求法可以使用单调队列/单调栈来实现。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5E5 + 5;
struct node {
    int x, y;
} b[N];
int n, a[N], k, st[N], top;
double ans;
bool check(int i, int j, int k) {
    return (b[j].y - b[i].y) * (b[k].x - b[j].x) > (b[k].y - b[j].y) * (b[j].x - b[i].x);
}
signed main() {
    scanf("%lld%lld", &n, &k);
    for (int i = 1, p = 0; i <= n; i++) {
        scanf("%lld", a + i);
        p += a[i];
        b[i] = { i, p };
    }
    for (int i = k; i <= n; i++) {
        while (top > 1 && check(st[top - 1], st[top], i - k)) top--;
        st[++top] = i - k;
        int l = 1, r = top;
        while (l < r) {
            int mid = l + r >> 1;
            if (check(st[mid], i, st[mid + 1]))
                l = mid + 1;
            else
                r = mid;
        }
        ans = max(ans, 1.0 * (b[i].y - b[st[l]].y) / (b[i].x - b[st[l]].x));
    }
    printf("%.2lf", ans);
    return 0;
}

这个也非常的好求,那如何在dp 内优化呢?

这里引入一种 dp 方程是有一项是跟i j 都有关的,例如 dpi=dpj+(aiaj)2

这个就可以直接化成dpi=dpj+a2i+a2j2aiaj

移项可得 dpj+a2j=dpia2i+2aiaj

y=dpi+a2i,x=ai,k=ai2

假设 k 不单调,使用单调栈加二分的算法。

如果单调,就可以直接单调队列。

而如果x 不单调,就可以直接用cdq分治的方法来写。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1E5 + 5;
int n, h[N], w[N], p[N], dp[N], st[N];
struct node {
    int x, y;
    bool operator<(const node &a) const {
        if (x != a.x)
            return x < a.x;
        return y < a.y;
    }
} b[N], a[N];
bool check(int i, int j, int k) {
    return (b[i].y - b[j].y) * (b[j].x - b[k].x) <= (b[j].y - b[k].y) * (b[i].x - b[j].x);
}
inline int calc(int i, int j) { return b[j].y + h[i] * h[i] + p[i - 1] - 2 * h[i] * b[j].x; }
void cdq(int l, int r) {
    if (l == r)
        return;
    int mid = l + r >> 1;
    cdq(l, mid);
    for (int i = l; i <= mid; i++) b[i] = a[i];
    sort(b + l, b + mid + 1);
    int top = 0;
    for (int i = l; i <= mid; i++) {
        while (top > 1 && check(i, st[top], st[top - 1])) top--;
        st[++top] = i;
    }
    for (int i = mid + 1; i <= r; i++) {
        int L = 1, R = top;
        while (L < R) {
            int mid = L + R >> 1;
            if (calc(i, st[mid]) > calc(i, st[mid + 1]))
                L = mid + 1;
            else
                R = mid;
        }
        dp[i] = min(dp[i], calc(i, st[L]));
        a[i] = { h[i], dp[i] + h[i] * h[i] - p[i] };
    }
    cdq(mid + 1, r);
}
signed main() {
    scanf("%lld", &n);
    for (int i = 1; i <= n; i++) scanf("%lld", h + i);
    for (int i = 1; i <= n; i++) scanf("%lld", w + i), p[i] = p[i - 1] + w[i];
    memset(dp, 0x3f, sizeof dp);
    dp[1] = 0;
    a[1] = { h[1], h[1] * h[1] - p[1] };
    cdq(1, n);
    cout << dp[n];
    return 0;
}

Building Bridges cdq + 单调栈。时间复杂度当然是 (nlog2n)

posted @ 2025-02-09 12:47  hnczy  阅读(8)  评论(0编辑  收藏  举报