题意:一篇文章打印成多行,每行可以有任意数目个字符,打印成一行的代价为sigma(ci)^2+m,求总的最小代价。
思路:设dp[i]为考虑前i个字符的最小代价,s[i]为从第1个字符到第i个字符的代价和。
那么枚举与第i个字符同行的字符个数得方程:dp[i] = min(dp[i] , dp[j] + (s[i] - s[j])^2 + m)。
然后斜率优化复杂度降到O(n)。
斜率DP个人理解:
在得到一个方程后,设变量k,j(正推k > j,逆推k<j),k比j更优,列出和斜率相关的方程。
斜率上的X,Y为决策点。将X,Y代入原始方程会得到一个直线,再根据具体意义和是否满足单调性来确定维护决策点的凹凸折线。
我的代码:
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long LL;
const int maxn = 500005;
int n,m,cost[maxn];
int dp[maxn],sum[maxn];
int que[maxn],head,tail;
int getDP(int i,int j){
return dp[j] + (sum[i]-sum[j])*(sum[i]-sum[j]) + m;
}
int getUp(int i,int j){
return dp[j] + sum[j]*sum[j] - dp[i] - sum[i]*sum[i];
}
int getDown(int i,int j){
return sum[j] - sum[i];
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++){
scanf("%d",&cost[i]);
sum[i] = sum[i-1] + cost[i];
}
dp[0] = head = tail = 0;
que[tail++] = 0;
for(int i=1;i<=n;i++){
while(head+1 < tail && getUp(que[head],que[head+1])
<= 2*sum[i]*getDown(que[head],que[head+1])) head++;
dp[i] = getDP(i,que[head]);
while(head+1 < tail && getDown(que[tail-1],i)*getUp(que[tail-2],que[tail-1])
>= getUp(que[tail-1],i)*getDown(que[tail-2],que[tail-1])) tail--;
que[tail++] = i;
}
printf("%I64d\n",dp[n]);
}
return 0;
}