HDU-3507 斜率DP
第一次写斜率dp,详细记录一下自己的想法当笔记吧,虽然不一定对。
HDU-3507 ,给出数列
a[i],打印连续数列段
k到
j代价为
(sum[j]?sum[k?1])2+m,求打印全部数列最小代价。
比较
k+1, j+1哪个更适合和
i放一起,
k+1
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)?(dp[k]+s[k]2)<=2×(s[j]?s[k])×s[i]
K=2×(s[j]?s[k])×s[i](dp[j]+s[j]2)?(dp[k]+s[k]2)?<=s[i]
即:
K
j+1更合适。
一至三点
k x。 下凸: K1? 1) K1? i最优 2) K1? j最优 3) s[x] k最优 故可采用维持队列下凸,仅比较相邻点可找到最优,即在 O(n)内完成。 下凹: K1?>K2?: 1) s[x]>K1?>K2?, i最优 2) K1?>s[x]>K2?, k, i都比 j优,需再比较 Kk, i?与 s[x]判断最终谁优。 3) K1?>K2?>s[x], k最优。 故可采用维持队列下凸,需比较任意两点可找到最优,即在 O(n2)内完成。 本题采用 O(n2)超时,故维护下凸即可。 证明如下操作后,最终中间过程以及最终获得队列必为下凸:采用数学归纳。
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
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]);
}
}