Print Article
Time Limit: 9000/3000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)Total Submission(s): 12253 Accepted Submission(s): 3758
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
![](https://i-blog.csdnimg.cn/blog_migrate/deb59af46e26501d1335903fa890b00f.jpeg)
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个数字a[N],输出的时候可以连续的输出,每连续输出一串,它的费用是 “这串数字和的平方加上一个常数M”。n<=500000
设dp[i]表示输出到i的时候最少的花费,sum[i]表示从a[1]到a[i]的数字和。
dp[i]=dp[j]+M+(sum[i]-sum[j])^2
设k<j<i。如果在j的时候决策要比在k的时候决策好,那么也是就是dp[j]+M+(sum[i]-sum[j])^2<dp[k]+M+(sum[i]-sum[k])^2。
两边移项一下,得到:(dp[j]+sum[j]^2-(dp[k]+sum[k]^2))/(2*(sum[j]-sum[k]))<sum[i]。我们把dp[j]+sum[j]^2看做是yj,把2*sum[j]看成是xj。
说明g[j,k]=(yj-yk)/(xj-xk)<sum[i]代表这j的决策比k的决策要更优。
若k<j<i且g[i,j]<g[j,k],则j点永远不可能成为最优解。
分三种情况讨论:
设当前点为a
1.如果g[i,j]与g[j,k]均小于sum[a],则i比j优,j比k优
2.如果g[i][j]与g[j,k]均大于sum[a],则k比j优,j比i优。
3.如果g[i][j]<sum[a]且g[i][j]>sum[a],则i比j优,k比j优。
不论如何,j都无法成为最佳决策点,所以可以排除j。
于是,所有的决策点满足一个下凸包性质。
设k<j<i。
由于我们排除了g[i,j]<g[j,k]的情况,所以整个有效点集呈现一种下凸性质,即g[i,j]>g[j,k]。
这样,从左到右,斜率之间就是单调递增的了。当我们的最优解取得在j点的时候,那么k点不可能再取得比j点更优的解了,于是k点也可以排除。换句话说,j点之前的点全部不可能再比j点更优了,可以全部从解集中排除。
1,用一个单调队列来维护解集。
2,假设队列中从头到尾已经有元素a b c。那么当d要入队的时候,我们维护队列的下凸性质,即如果g[d,c]<g[c,b],那么就将c点删除。直到找到g[d,x]>=g[x,y]为止,并将d点加入在该位置中。
3,找最佳决策点时,设当前求解状态为i,从队头开始,如果已有元素a b c,当i点要求解时,如果g[b,a]<sum[i],那么说明b点比a点更优,a点可以排除,于是a出队,直到第一次遇到g[j,j-1]>sum[i],此时j-1即为最佳决策点。
Code:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int Max = 500000;
int N, M;
int sum[Max + 5], Q[Max + 5], Dp[Max + 5];
bool getint(int & num){
char c; int flg = 1; num = 0;
while((c = getchar()) < '0' || c > '9'){
if(c == '-') flg = -1;
if(c == -1) return 0;
}
while(c >= '0' && c <= '9'){
num = num * 10 + c - 48;
if((c = getchar()) == -1) return 0;
}
num *= flg;
return 1;
}
int nume(int j, int k){ return Dp[j] + sum[j] * sum[j] - Dp[k] - sum[k] * sum[k];}
int deno(int j, int k){ return 2 * (sum[j] - sum[k]);}
int Get_Dp(int i, int j){return Dp[j] + M + (sum[i] - sum[j]) * (sum[i] - sum[j]);}
int main(){
while(getint(N) && getint(M)){
//Dp[0] = sum[0] = 0;
for(int i = 1; i <= N; ++ i) getint(sum[i]), sum[i] += sum[i - 1];
int fro = 1, bac = 0;
Q[++ bac] = 0;
for(int i = 1; i <= N; ++ i){
while(fro < bac && nume(Q[fro+1], Q[fro]) < sum[i] * deno(Q[fro+1], Q[fro]))
++ fro;
Dp[i] = Get_Dp(i, Q[fro]);
while(fro < bac && nume(i, Q[bac]) * deno(Q[bac], Q[bac-1]) <= nume(Q[bac], Q[bac-1]) * deno(i, Q[bac]))
//while(fro < bac && nume(i, Q[bac-1]) * deno(Q[bac], Q[bac-1]) <= nume(Q[bac], Q[bac-1]) * deno(i, Q[bac-1]))
-- bac;
Q[++ bac] = i;
//判断进队的时候必须有"="号,假设现在有i和j的斜率与j和k斜率相等,我们必须把j删除,因为可能在k之前还有点p,使得k也可以被删除,而删除掉j并不会使答案变坏。
//以上这种判断出队的方法是建立在图像的基础上的,目的是维护一个下凸包,所以将g[i ,j]与g[j, k]比较
//而建立在g[i,j]的定义上,还可以写成另一种,因为目的是维护下凸,所以只是要判断当i进入队列后,队尾的值是不是最优的(即下凸)
//那么完全可以直接比较g[i, j]和g[i, j-1]的斜率,这样更直观,也是正确的,但同样存在"="的问题。可以写成上方注释掉的代码。
}
printf("%d\n", Dp[N]);
}
return 0;
}