procedure2012
It's not worth it to know you're not worth it!

[关键字]:动态规划 斜率优化

[题目大意]:自己搜吧

//===============================================================================

[分析]:首先写出50分的程序,转移方程是:f[i]=max{f[j]+g(s[j]-s[i])}g()就是那个二次函数。然后将方程变形,设两个策略x、y(0<=x<y<i),若y一定比x优:

f[x]+g(s[i]-s[x])<f[y]+g(s[i]-s[y])

=f[x]+a*(s[i]-s[x])2+b*(s[i]-s[x])+c<f[y]+a*(s[i]-s[y])2+b*(s[i]-s[y])+c

=f[x]+a*s[x]^2-2*a*s[i]*s[x]-b*s[x]>f[y]+a*s[y]^2-2*a*s[i]*s[y]-b*s[y]

=(f[x]+a*s[x]^2-b*s[x])-(f[y]+a*s[y]^2-b*s[y])>2*a*s[i]*(s[x]-s[y])

设h(x)=f[x]+a*s[x]^2-b*s[x]

=(h(x)-h(y))/(s[x]-s[y])<2*a*s[i](s[x]-s[y]<0)

=(h(y)-h(x))/(s[y]-s[x])<2*a*s[i]

设K(x,y)=(h(y)-h(x))/(s[y]-s[x])这样就可一看成点(h(x),s[x])和(h(y),s[y])的斜率小于2*a*s[i]。这样对于最优解k

在它左边的点i:K(i,k)<=2*a*s[i]

在它左边的点i:K(k,i)>2*a*s[i]

这样k一定在当前所有点的上凸壳上,又因为斜率2*a*s[i]递减所以新加入点与上一个点的斜率一定也要递减,所以可以用单调队列维护可行解。每次需要转移时查找单调队列q,如果K(q[h],q[h+1])<=2*a*s[i]就以q[h]转移否则h++只到找到符合条件的状态或队列里只剩一个。新转移的状态入队时也要判断新加入点与上一个点的斜率是否也递减,维护队列单调性。

[代码]:

View Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const double EP=1e-8;

int n,h,t;
int q[10000000];
long long x,A,B,C;
long long s[10000000],f[10000000];

double Calc(int x,int y)
{
return (double)((f[y]+A*s[y]*s[y]-B*s[y])-(f[x]+A*s[x]*s[x]-B*s[x]))/(s[y]-s[x]);
}

int main()
{
freopen("in3.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%d",&n);
scanf("%lld%lld%lld",&A,&B,&C);
for (int i=1;i<=n;i++)
scanf("%lld",&x),s[i]=s[i-1]+x;
h=t=1;
for (int i=1;i<=n;i++)
{
while (h<t && Calc(q[h],q[h+1])-2.0*A*s[i]>EP) h++;
f[i]=f[q[h]]+A*(s[i]-s[q[h]])*(s[i]-s[q[h]])+B*(s[i]-s[q[h]])+C;
q[++t]=i;
while (t-2>=h && Calc(q[t-1],q[t])-Calc(q[t-2],q[t-1])>EP) q[t-1]=q[t],--t;
}
printf("%lld\n",f[n]);
return 0;
}



posted on 2012-03-09 17:40  procedure2012  阅读(895)  评论(0编辑  收藏  举报