hdu3507(初识斜率优化DP)

hdu3507

题意

给出 N 个数字,输出的时候可以选择连续的输出,每连续输出一串,它的费用是 这串数字和的平方加上一个常数 M。

分析

斜率优化dp,入门题。

参考
参考

得到 dp 方程后,发现是O(n * n)的复杂度,且 n 很大,考虑用斜率优化。

设 s[i] 为 1 到 i 的花费之和。
dp方程:\(dp[i] = dp[j] + (s[i] - s[j])^2 + m ( i > j)\)
如果选 j 比选 k 更优,则有 \(dp[j] + (s[i] - s[j])^2 + m < dp[k] + (s[i] - s[k])^2 + m\)
移项可得, $(dp[j] + s[j]^2 - (dp[k] + s[k]^2)) / (2 * (s[j] - s[k])) < s[i] $
这样形成类似于求斜率的式子。
那么满足这个式子,是由于前提条件:j 比 k 更优,即选 j 花费更少。

令 $ g[j, l] = (dp[l] + s[l]^2 - (dp[j] + s[j]^2)) / (2 * (s[l] - s[j]))$

code

#include<cstdio>
using namespace std;
const int MAXN = 5e5 + 5;
int s[MAXN], q[MAXN], dp[MAXN];
int n, m;
int up(int k, int j) {
    return dp[j] + s[j] * s[j] - (dp[k] + s[k] * s[k]);
}
int down(int k, int j) {
    return 2 * (s[j] - s[k]);
}
int get(int i, int j) {
    return dp[j] + (s[i] - s[j]) * (s[i] - s[j]) + m;
}
int main() {
    while(~scanf("%d%d", &n, &m)) {
        s[0] = 0;
        dp[0] = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%d", &s[i]);
            s[i] += s[i - 1];
        }
        int head = 0, tail = 0;
        q[tail++] = 0;
        for(int i = 1; i <= n; i++) {
            while(head + 1 < tail && up(q[head], q[head + 1]) <= s[i] * down(q[head], q[head + 1]))
                head++;
            dp[i] = get(i, q[head]);
            // g[k,j] >= g[j,l] (k < j < l) 说明 j 可以舍去
            while(head + 1 < tail && up(q[tail - 2], q[tail - 1]) * down(q[tail - 1], i) >= up(q[tail - 1], i) * down(q[tail - 2], q[tail - 1]))
                tail--;
            q[tail++] = i;
        }
        printf("%d\n", dp[n]);
    }
    return 0;
}
posted @ 2017-06-13 16:09  ftae  阅读(375)  评论(0编辑  收藏  举报