Print Article /// 斜率优化DP oj26302

时间:2021-05-20 09:50:28

题目大意:

典题

数学分析 G(a,b)<sum[i]时 a优于b

G(a,b)<G(b,c)<sum[i]时 b必不为最优

#include <bits/stdc++.h>
#define N 500005
using namespace std;
int n,m,dp[N],deq[N],sum[N];
// deq[]为单调队列 sum[]为数组的前缀和
int DP(int i,int j) {
return dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]);
}
int UP(int j,int k) { //yj-yk的部分
return dp[j]+sum[j]*sum[j]-(dp[k]+sum[k]*sum[k]);
}
int DOWN(int j,int k) {//xj-xk的部分
return *(sum[j]-sum[k]);
}
/*
由分析 当0<a<b<i时
若(ya-yb)/(xa-xb)<sum[i]
此处表达为G(a,b)<sum[i] 则j优于k 若存在a,b和b,c满足上述要求
即存在G(a,b)<sum[i] G(b,c)<sum[i]
若G(a,b)<G(b,c) 则b肯定不为最优点
*/
int main()
{
while(~scanf("%d%d",&n,&m)) {
sum[]=dp[]=;
for(int i=;i<=n;i++) {
int num; scanf("%d",&num);
sum[i]=sum[i-]+num;
}
int head=, tail=;
deq[tail++]=;
for(int i=;i<=n;i++) {
while(head+<tail && UP(deq[head+],deq[head])<=sum[i]
*DOWN(deq[head+],deq[head]))
head++; /// G(head+1,head)<=sum[i] 即head+1优于head 则去掉head
dp[i]=DP(i,deq[head]); // 用此时的最优head更新dp[i]
while(head+<tail && UP(i,deq[tail-])*DOWN(deq[tail-],deq[tail-])
<=DOWN(i,deq[tail-])*UP(deq[tail-],deq[tail-]))
tail--;
/// 如果此时G(i,tail-1)<=G(tail-1,tail-2)<=sum[i] 则说明tail-1对应点为可删去
deq[tail++]=i;
}
printf("%d\n",dp[n]);
}
return ;
}
/*
纠结了一下维护单调队列时为什么判断条件是<=
第一处 G(head+1,head)=sum[i] 说明 两者平等 不存在谁更优这个问题
而仍然 head++; 是因为既然两者平等 那么只要留一个就可以了
第二处 G(i,tail-1)=G(tail-1,tail-2) 说明 两者斜率相等
即 i,tail-1,tail-2 三个对应点在同一条直线上
那么 tail-1 这个点可以直接忽略 所以继续 tail--;
*/