听说是挺模板的一道题,然而我还是理解了好半天(太菜了233)
数学推演过程在这->http://www.cnblogs.com/ljh2000-jump/p/6015002.html(发照片好麻烦就不发了233)
大概斜率优化的整体过程是这样(针对于单调的那种):
1.写出dp方程;
2.推导当前状态i有关前面节点的式子,得到一个无关于i的类似于斜率形式的多项式。这里需要保证不等式另一端必须是只含i的单调式。
实现方法:
1.判断队列头几项是否满足当前状态的不等式(斜率在队列中不减),若不满足,则head+1必然比head优,于是舍弃;
2.更新dp[i]的值;
3.判断当前点是否可以进入队列,若要满足上凸函数的性质,则不断判断后两项与当前节点是否能组成上凸函数,若不能,则尾节点必定不比当前节点优,可以删去(斜率越大越有差的趋势)。
注意精度,爆了好几次。
代码如下:
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000015;
int n;
int x[maxn],c[maxn],p[maxn],q[maxn];
long long f[maxn],sp[maxn],smul[maxn];
double getk(int x,int y)
{
return (double)((f[x]-f[y])+(smul[x]-smul[y]))/(double)(sp[x]-sp[y]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",x+i,p+i,c+i);
sp[i]=sp[i-1]+p[i];
smul[i]=smul[i-1]+(long long)x[i]*p[i];
}
int head=1,rear=1;
for(int i=1;i<=n;i++)
{
while(head<rear&&getk(q[head+1],q[head])<x[i])head++;
f[i]=f[q[head]]+x[i]*(sp[i]-sp[q[head]])-(smul[i]-smul[q[head]])+c[i];
while(head<rear&&getk(q[rear],q[rear-1])>getk(i,q[rear]))rear--;
q[++rear]=i;
}
printf("%lld",f[n]);
}