题意:直接看题意即可
而本篇主要介绍这个题的推导过程,首先设dp[i]为到了第i个点的最小花费,得到
dp[i]=min(dp[j]+(a[i]-a[j+1])^2)+c(j<i),展开后得到
dp[i]=min(dp[j]+a[i]^2-2*a[i]a[j+1]+a[j+1]^2)+c,将无关项从min中拿出来,得到
dp[i]=min(dp[j]-2*a[i]a[j+1]+a[j+1]^2)+a[i]^2+c,这样现在主要分析下min里的东西
设y=dp[j]+a[j+1]^2,x=a[j+1],k=2*a[i],然后设
G=y-k*x,稍微变形下
y=k*x+G,为了使得dp[j]最小,也就是为了使得G最小,因为(x,y)在以前我们都推出来了,所以直接用单调队列维护下凸包跑出答案即可,具体证明见上面链接。
附上代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll getll()
{
char ch=' ';
while(ch<'0'||ch>'9'){
ch=getchar();
}
ll x=0;
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';ch=getchar();
}
return x;
}
const int maxn=1e6+5;
struct point{
ll x,y;
point(ll x=0,ll y=0):x(x),y(y){}
};
point q[maxn];
ll multi(point o,point a,point b)
{
return (a.x-o.x)*(b.y-o.y)-(a.y-o.y)*(b.x-o.x);
}
ll dp[maxn];
int head,tail;
ll x[maxn];
int n,c;
inline ll sqr(ll x)
{
return x*x;
}
int main()
{
while(scanf("%d%d",&n,&c)&&(n||c)){
memset(x,0,sizeof(x));
memset(dp,0,sizeof(dp));
for(int i=0;i<n;i++){
x[i]=getll();
}
head=tail=0;
q[tail++]=point(x[0],sqr(x[0]));
dp[0]=c;
for(int i=1;i<n;i++){
point pp(x[i],dp[i-1]+sqr(x[i]));
while(head+1<tail&&multi(q[tail-2],q[tail-1],pp)<0){
tail--;
}
q[tail++]=pp;
while(head+1<tail&&q[head].y-2*x[i]*q[head].x>=q[head+1].y-2*x[i]*q[head+1].x){
head++;
}
dp[i]=q[head].y-2*x[i]*q[head].x+x[i]*x[i]+c;
}
printf("%I64d\n",dp[n-1]);
}
return 0;
}