HDU-3507


HDU-3507 斜率DP

第一次写斜率dp,详细记录一下自己的想法当笔记吧,虽然不一定对。
HDU-3507 ,给出数列

a[i]a[i]

a[i],打印连续数列段

kk

k到

jj

j代价为

(sum[j]?sum[k?1])2+m(sum[j]-sum[k-1])^2+m

(sum[j]?sum[k?1])2+m,求打印全部数列最小代价。

比较

k+1, j+1k+1,\ j+1

k+1, j+1哪个更适合和

ii

i放一起,

k+1<j+1<ik+1

k+1

dp[j]+(s[i]?s[j])2+m<=dp[k]+(s[i]?s[k])2+mdp[j]+(s[i]-s[j])^2+m<=dp[k]+(s[i]-s[k])^2+m

dp[j]+(s[i]?s[j])2+m<=dp[k]+(s[i]?s[k])2+m

dp[j]+s[j]2?2×s[i]×s[j]<=dp[k]+s[k]2?2×s[i]×s[k]dp[j]+s[j]^2-2\times s[i]\times s[j]<=dp[k]+s[k]^2-2\times s[i]\times s[k]

dp[j]+s[j]2?2×s[i]×s[j]<=dp[k]+s[k]2?2×s[i]×s[k]

(dp[j]+s[j]2)?(dp[k]+s[k]2)<=2×(s[j]?s[k])×s[i](dp[j]+s[j]^2)-(dp[k]+s[k]^2)<=2\times(s[j]-s[k])\times s[i]

(dp[j]+s[j]2)?(dp[k]+s[k]2)<=2×(s[j]?s[k])×s[i]

K=(dp[j]+s[j]2)?(dp[k]+s[k]2)2×(s[j]?s[k])×s[i]<=s[i]K=\frac{(dp[j]+s[j]^2)-(dp[k]+s[k]^2)}{2\times(s[j]-s[k])\times s[i]}<=s[i]

K=2×(s[j]?s[k])×s[i](dp[j]+s[j]2)?(dp[k]+s[k]2)?<=s[i]

即:

K<s[i]K

Kj+1j+1

j+1更合适。

一至三点

k<j<ik

kxx

x。

下凸:

K1<K2K_1

K1?

1)

K1<K2<s[x]K_1

K1?ii

i最优

2)

K1<s[x]<K2K_1

K1?jj

j最优

3)

s[x]<K1<K2s[x]

s[x]kk

k最优

故可采用维持队列下凸,仅比较相邻点可找到最优,即在

O(n)O(n)

O(n)内完成。

下凹:

K1>K2K_1>K_2

K1?>K2?:

1)

s[x]>K1>K2s[x]>K_1>K_2

s[x]>K1?>K2?,

ii

i最优

2)

K1>s[x]>K2K_1>s[x]>K_2

K1?>s[x]>K2?,

k, ik,\ i

k, i都比

jj

j优,需再比较

Kk, iK_{k,\ i}

Kk, i?与

s[x]s[x]

s[x]判断最终谁优。

3)

K1>K2>s[x]K_1>K_2>s[x]

K1?>K2?>s[x],

kk

k最优。

故可采用维持队列下凸,需比较任意两点可找到最优,即在

O(n2)O(n^2)

O(n2)内完成。

本题采用

O(n2)O(n^2)

O(n2)超时,故维护下凸即可。

证明如下操作后,最终中间过程以及最终获得队列必为下凸:采用数学归纳。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
ll dp[maxn], a[maxn], s[maxn], m;
int q[maxn], n;
ll up(int j, int k){
    return (dp[j]+s[j]*s[j])-(dp[k]+s[k]*s[k]);
}
ll dow(int j, int k){
    return 2*(s[j]-s[k]);
}
ll sol(int j, int i){//选j+1到i
    return dp[j]+m+(s[i]-s[j])*(s[i]-s[j]);
}
int main(){
    while(scanf("%d%lld", &n, &m)!=EOF){
        for(int i=1; i<=n; i++){
            scanf("%lld", &a[i]);
            s[i]=s[i-1]+a[i];
        }
        int st=0, t=0;
        q[t++]=0;
        for(int i=1; i<=n; i++){
            while(st+1<t && up(q[st+1], q[st])<=s[i]*dow(q[st+1], q[st]))
                st++;
            dp[i]=sol(q[st], i);
            while(st+1<t && up(i, q[t-1])*dow(q[t-1], q[t-2])<=
                up(q[t-1], q[t-2])*dow(i, q[t-1])) t--;
            q[t++]=i;
        }
        printf("%lld\n", dp[n]);
    }
}