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
就是输出序列a[n],每连续输出的费用是连续输出的数字和的平方加上常数M
让我们求这个费用的最小值。
设dp[i]表示输出前i个的最小费用,那么有如下的DP方程:
dp[i]= min{ dp[j]+(sum[i]-sum[j])^2 +M } 0<j<i
其中 sum[i]表示数字的前i项和。
相信都能理解上面的方程。
直接求解上面的方程的话复杂度是O(n^2)
对于500000的规模显然是超时的。下面讲解下如何用斜率优化DP使得复杂度降低一维。
我们首先假设在算 dp[i]时,k<j ,j点比k点优。
也就是
dp[j]+(sum[i]-sum[j])^2+M <= dp[k]+(sum[i]-sum[k])^2+M;
所谓j比k优就是DP方程里面的值更小
对上述方程进行整理很容易得到:
[(dp[j]+sum[j]*sum[j])-(dp[k]+sum[k]*sum[k])] / 2(sum[j]-sum[k]) <=sum[i].
注意整理中要考虑下正负,涉及到不等号的方向。
左边我们发现如果令:yj=dp[j]+sum[j]*sum[j] xj=2*sum[j]
那么就变成了斜率表达式:(yj-yk)/(xj-xk) <= sum[i];
而且不等式右边是递增的。
所以我们可以看出以下两点:我们令g[k,j]=(yj-yk)/(xj-xk)
第一:如果上面的不等式成立,那就说j比k优,而且随着i的增大上述不等式一定是成立的,也就是对i以后算DP值时,j都比k优。那么k就是可以淘汰的。
第二:比较i与点q[tail-1],q[tail-1]与点q[tail-2]的斜率k1,k2,
- 比较i与点q[tail-1],q[tail-1]与点q[tail-2]的斜率k1,k2,
- 若k1<=k2则将点q[tail-1]去除,因为后面肯定是先k1<sum[i+x]
- 也就是点i肯定比q[tail-1]更优
- 然后重复比较直到k1>k2
#include <iostream>
#include <algorithm>
#include <cstring>
#include<cstdio>
#include<string>
#include<cmath>
#include<cstdlib>
#include<vector>
#define LL long long
using namespace std;
const int MAXN=500010;
LL sum[MAXN];
LL dp[MAXN];
LL q[MAXN];
int a[MAXN];
LL getup(int j,int k)
{
return (dp[j]+sum[j]*sum[j])-(dp[k]+sum[k]*sum[k]);
}
LL getdown(int j,int k)
{
return 2*(sum[j]-sum[k]);
}
int main()
{
int n;
while(cin>>n)
{
int m;
cin>>m;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)
sum[i]+=(sum[i-1]+a[i]);
memset(dp,0,sizeof(dp));
int head=0,tail=0;
q[tail++]=0;
for(int i=1;i<=n;i++)
{
while(head+1<tail&&getup(q[head+1],q[head])<=sum[i]*getdown(q[head+1],q[head]))
head++;
dp[i]=dp[q[head]]+m+(sum[i]-sum[q[head]])*(sum[i]-sum[q[head]]);
while(head+1<tail&&getup(i,q[tail-1])*getdown(q[tail-1],q[tail-2])<=getup(q[tail-1],q[tail-2])*getdown(i,q[tail-1]))
tail--;
q[tail++]=i;
}
cout<<dp[n]<<endl;
}
return 0;
}