Print Article
Total Submission(s): 10556 Accepted Submission(s): 3227
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,问如何输出才能使总消耗最小
http://www.cnblogs.com/kuangbin/archive/2012/08/26/2657650.html
思路:
设dp[i]为前i个数的最小花费,那么有dp[i] = min(dp[j]+(sum[i]-sum[j])²+m)(0<j<i)
可这样循环是n²的,那我们假设k<j<i,从j+1点做起始点比从k+1点做起始点更优,则有公式
dp[j]+(sum[i]-sum[j])²<=dp[k]+(sum[i]-sum[k])²,移项得
((dp[j]+sum[j]*sum[j])-(dp[k]+sum[k]*sum[k]))/(2*(sum[j]-sum[k]))<=sum[i],
那么就得到了斜率表达式g[k,j]=Gp[k,j]/Gd[k,j],如果g[k,j]<=sum[i],则从j+1点做起始点比从k+1点做起始点更优
除此之外,易得如果g[k,j]>=g[j,i]那么j可淘汰。②
这样下来,可以确定一个j点,他前面所有的点都不可能比它更优(sum[i]递增),但是后面的怎么办?很显然
此时j点比j+1点优,如果j+1点比j+2点优,那么就没有什么可说的,但如果j+1点没有j+2点优,那么当j+2点入队时,
j+1点就会通过式②被淘汰!这样j点一定是对于当前i点最优的!
那么只要维护一个队列就好,如果当前队尾的两个位置k,j满足g[k,j]<=sum[i],则可以将队尾k弹出,
如果当前队头的两个位置k,j满足g[k,j]>=g[j,i],则可以将队头j弹出,i表示当前遍历到第i个数
#include<stdio.h>
#include<algorithm>
using namespace std;
int m, sum[500005], q[500005], dp[500005];
int Getup(int x, int y)
{
return dp[y]+sum[y]*sum[y]-dp[x]-sum[x]*sum[x];
}
int Getdown(int x, int y)
{
return 2*(sum[y]-sum[x]);
}
int main(void)
{
int n, i, b, e;
while(scanf("%d%d", &n, &m)!=EOF)
{
sum[0] = dp[0] = 0;
for(i=1;i<=n;i++)
{
scanf("%d", &sum[i]);
sum[i] += sum[i-1];
}
b = e = 0;
q[e++] = 0;
for(i=1;i<=n;i++)
{
while(b+1<e && Getup(q[b], q[b+1])<=sum[i]*Getdown(q[b], q[b+1])) b++;
dp[i] = dp[q[b]]+(sum[i]-sum[q[b]])*(sum[i]-sum[q[b]])+m;
while(b+1<e && Getup(q[e-1], i)*Getdown(q[e-2], q[e-1])<=Getup(q[e-2], q[e-1])*Getdown(q[e-1], i)) e--;
q[e++] = i;
}
printf("%d\n", dp[n]);
}
return 0;
}