2016 UESTC Training for Dynamic Programming Q - 柱爷的宝藏 斜率优化

Q - 柱爷的宝藏

Time Limit: 1000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)
Submit  Status

众所周知,柱爷既会炒股,又会抢银行,所以柱爷很有钱。

传说柱爷有 N N份宝藏,但没人知道它们藏在哪里。因为柱爷用特殊的方式把它们藏在了多个地方。

柱爷先把编号为 1..N 1..N的宝藏按顺序排成一排,每份价值为 Ai Ai。然后把它们合并成许多份,只有相邻的几个宝藏才能合成一份。每份宝藏放

在一个地方。

显然,每份宝藏的价值越高,越不安全。柱爷认为,假设某份宝藏由 Al..r Al..r组成,这份宝藏被发现的概率可用下面的公式算出,其中 M M是一个

常数。

(ri=lAi)2+M (∑i=lrAi)2+M

这个值越大,表示越有可能被发现。所以柱爷希望所有宝藏被发现概率之和最小,请问这个最小值是多少。

Input

第一行输入两个数 NM N,M

第二行输入 N N个数,表示每个宝藏的价值。

数据保证:

  • 1N5000000M1000 1≤N≤500000,0≤M≤1000

  • 0Ai1000 0≤Ai≤1000

Output

输出一行,概率之和的最小值

Sample input and output

Sample Input Sample Output
5 6
5 9 5 1 2
164

Hint

样例:

划分为 5|9|5|1,2 5|9|5|1,2

[52+6]+[92+6]+[52+6]+[(1+2)2+6]=164

Source

2016 UESTC Training for Dynamic Programming


My Solution

dp[i] = min(dp[j] + (s[i]-s[j])^2+M), 0<=j<i
单调队列

dp[i] = min(dp[j] + s[j]^2 - 2*s[j]*s[i]) +s[i]^2+M

min(dp[j] + s[j]^2 - 2*s[j]*s[i])

令 q<p<i 
P[q].x = 2*sum[q] P[q].y = dp[q] + sum[q]*sum[q];
P[p].x = 2*sum[p] P[p].y = dp[p] + sum[p]*sum[p];

xy = q;
que.front() = p;

可以证明这时(P[que.front()].y - P[xy].y) <= sum[i]*(P[que.front()].x - P[xy].x)

初始化
que.push_back(0);
    dp[0] = 0;
    P[0].y = 0;//printf("dp[%d] = %d\n", 0, dp[0]);
    P[0].x = 0;


然后
While(队首斜率<s[i]) 队首出队
// 保证队首为最优解

这样队首斜率 >= s[i]

复杂度 O(n)

#include <iostream>
#include <cstdio>
#include <deque>
using namespace std;
const int maxn = 500000 + 8;
long long dp[maxn], sum[maxn];
struct points{
    long long x, y;
}P[maxn];

deque<int> que;
//Runtime Error on test 6	除 0 !!!!!! 每次除以一个东西想下如果除以0会怎样
//val 可以等于 0 所以当然会出现除 0 汗
//如果单纯的把/变成*,乘上负数要变向......
//Wrong Answer on test 13
//这个和师父以前Debug了很久,原来是自己没有(0, 0)点这样,我第一次必定是分开的
//比如 1 6
//     1 1 我就得到了错误的答案,所以dp[x]是要从0 ~ x-1里找答案的
//         也就是第1个数从dp[0],sum[0]对应的(0, 0)点, 其它也有可能对应这个点
int main()
{
    #ifdef LOCAL
    freopen("a.txt", "r", stdin);
    #endif // LOCAL
    int N, M, xy;
    long long val;
    scanf("%d%d", &N, &M);
    //pretreat
    sum[0] = 0;
    for(int i = 1; i <= N; i++){
        scanf("%lld", &val);
        sum[i] = sum[i-1] + val;
        P[i].x = 2*sum[i];
    }

    que.push_back(0);
    dp[0] = 0;
    P[0].y = 0;//printf("dp[%d] = %d\n", 0, dp[0]);
    P[0].x = 0;
    for(int i = 1; i <= N; i++){
        xy = que.front();que.pop_front();
        while(!que.empty() && (P[que.front()].y - P[xy].y) <= sum[i]*(P[que.front()].x - P[xy].x)){
            xy = que.front();que.pop_front();
        }
        que.push_front(xy);

        dp[i] = P[xy].y - P[xy].x*sum[i] + sum[i]*sum[i] + M;    //P[xy].y - P[xy].x*sum[i] == min{dp[j] + s[j]^2 - 2*sum[j]*sum[i]}
        //printf("xy = %d, dp[%d] = %d\n", xy, i, dp[i]);
        P[i].y = dp[i] + sum[i]*sum[i];
        xy = que.back();que.pop_back();
        if(!que.empty() && (P[i].y - P[xy].y)*(P[xy].x - P[que.back()].x) > (P[xy].y - P[que.back()].y)*(P[i].x - P[xy].x)){
            que.push_back(xy);
            que.push_back(i);
        }
        else if(!que.empty()){                                                        // <=
            while(!que.empty() && (P[i].y - P[xy].y)*(P[xy].x - P[que.back()].x) <= (P[xy].y - P[que.back()].y)*(P[i].x - P[xy].x)){
                xy = que.back(); que.pop_back();
            }
            que.push_back(xy);
            que.push_back(i);
        }
        else{
            que.push_back(xy);
            que.push_back(i);
        }
    }
    printf("%lld", dp[N]);
    return 0;
}

Thank you!
                                                                                                                                               ------from  ProLights

你可能感兴趣的:(dp,ACM,for,Training,dp斜率优化)