hhz6830975
2018-01-18 14:07:54
这是一道经典的斜率优化入门题,就用这题来作个总结好了
前言:斜率优化的思想其实和高中数学的线性规划有相似之处,因此建议没学过的同学先了解一下线性规划
首先提一下单调队列优化:
当dp方程为
这时用单调队列可以将其优化为
而dp方程为
回到本题,设前缀和为
但这个方程是
(以下称两点斜率为
令
则
展开得
移项得
将
而对于每个
接下来的步骤和线性规划很相似
此时队首的点即为最优,根据它计算出
对队尾:
解释:
若
因为目标直线斜率单调递增,所以当前删去的
而操作3的理由如下:
图中红色的点为
显然,满足操作3的要求时,
以上即为本题算法
要注意,初始化时要加入单调队列的点为
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef double db;
typedef long long LL;
const int maxn=50010;
int n,L;
db sum[maxn],dp[maxn];
int head,tail,Q[maxn];
inline db a(int i){return sum[i]+i;}
inline db b(int i){return a(i)+L+1;}
inline db X(int i){return b(i);}
inline db Y(int i){return dp[i]+b(i)*b(i);}
inline db slope(int i,int j){return (Y(i)-Y(j))/(X(i)-X(j));}
int main(){
scanf("%d%d",&n,&L);
for(int i=1;i<=n;i++){
scanf("%lf",&sum[i]);
sum[i]+=sum[i-1];
}
head=tail=1;
for(int i=1;i<=n;i++){
while(head<tail&&slope(Q[head],Q[head+1])<2*a(i)) ++head;
dp[i]=dp[Q[head]]+(a(i)-b(Q[head]))*(a(i)-b(Q[head]));
while(head<tail&&slope(i,Q[tail-1])<slope(Q[tail-1],Q[tail])) --tail;
Q[++tail]=i;
}
printf("%lld\n",(LL)dp[n]);
return 0;
}