Print Article
Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 8589 Accepted Submission(s): 2677
One day Zero want to print an article which has N words, and each word i has a cost Ci to be printed. Also, Zero know that print k words in one line will cost
M is a const number.
Now Zero want to know the minimum cost in order to arrange the article perfectly.
5 5 5 9 5 7 5
230
题目大意:多组数据。每组数据n,m ,n 表示有n个数,m是一个常数。
输出N个数字a[N],输出的时候可以连续连续的输出,每连续输出一串,它的费用是 “这串数字和的平方加上一个常数M”。
题解:斜率优化dp
设f[i]表示输出到i的时候最少的花费,sum[i]表示从a[1]到a[i]的数字和。于是方程就是:
f[i]=f[j]+M+(sum[i]-sum[j])^2;
假设k<j<i。如果在j的时候决策要比在k的时候决策好,那么也是就是f[j]+M+(sum[i]-sum[j])^2<f[k]+M+(sum[i]-sum[k])^2。
把式子拆开,得到f[j]+M+sum[i]^2+sum[j]^2-2sum[i]sum[j]<f[k]+M+sum[i]^2+sum[k]^2-2sum[i]sum[k]
两边移项一下,得到:(f[j]+sum[j]^2-(f[k]+sum[k]^2))/(2*(sum[j]-sum[k]))<sum[i]。我们把f[j]-num[j]^2看做是yj,把2*sum[j]看成是xj。
就得到了yj-yk/xj-xk<sum[i] ,因为前面的假设是j的决策比k的决策要好,且k<j<i,所有得到的这个式子就表示
j的决策比k的决策要更优
斜率优化做法总结如下:
1,用一个单调队列来维护解集。
2,假设队列中从头到尾已经有元素a b c。那么当d要入队的时候,我们维护队列的上凸性质,即如果g[d,c]<g[c,b],那么就将c点删除。直到找到g[d,x]>=g[x,y]为止,并将d点加入在该位置中。
3,求解时候,从队头开始,如果已有元素a b c,当i点要求解时,如果g[b,a]<sum[i],那么说明b点比a点更优,a点可以排除,于是a出队。最后dp[i]=getDp(q[head])。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 500003
using namespace std;
int n,m,q[N],a[N],sum[N],f[N];
int y(int x,int y)
{
return f[y]-f[x]+sum[y]*sum[y]-sum[x]*sum[x];
}
int x(int x,int y)
{
return sum[y]-sum[x];
}
int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
memset(sum,0,sizeof(sum));
memset(f,0,sizeof(f));
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
}
int head=0,tail=0;
for (int i=1;i<=n;i++)
{
int k=2*sum[i];
while (head<tail&&y(q[head],q[head+1])<=k*x(q[head],q[head+1]))
head++;
f[i]=f[q[head]]+x(q[head],i)*x(q[head],i)+m;
while (head<tail&&y(q[tail-1],q[tail])*x(q[tail],i)>=y(q[tail],i)*x(q[tail-1],q[tail]))
tail--;
tail++; q[tail]=i;
}
printf("%d\n",f[n]);
}
return 0;
}