题目链接:hdu 3507 Print Article
题意:
每个字有一个值,现在让你分成k段打印,每段打印需要消耗的值用那个公式计算,现在让你求最小值
题解:
设dp[i]表示前i个字符需要消耗的最小值,那么有dp[i]=min{dp[k]+(sum[i]-sum[k])2+m)}(k<i)。
这样是n2 的做法。
考虑用斜率优化:
设k<j,对于dp[i],从k+1到i为一段比j+1到i为一段更优。
那么有
dp[j]+(sum[i]-sum[j])2+m<=dp[k]+(sum[i]-sum[k])2+m
整理得:
dp[j]+sum[j]*sum[j]-(dp[k]+sum[k]*sum[k])/sum[j]-sum[k]<=2*sum[i]。
不等式的右边就是一个斜率,然后用单调队列优化,做到O(n)的复杂度。
1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;++i) 3 using namespace std; 4 5 const int N=5e6+7; 6 int n,m,dp[N],sum[N],Q[N],head,tail; 7 8 int getx(int j,int k){return sum[j]-sum[k];} 9 int gety(int j,int k){return dp[j]+sum[j]*sum[j]-dp[k]-sum[k]*sum[k];} 10 int check(int i,int j,int k){return gety(i,j)*getx(j,k)<=gety(j,k)*getx(i,j);} 11 12 int main() 13 { 14 while(~scanf("%d%d",&n,&m)) 15 { 16 F(i,1,n)scanf("%d",sum+i),sum[i]+=sum[i-1]; 17 head=1,tail=0; 18 Q[++tail]=0; 19 F(i,1,n) 20 { 21 while(head<tail&&gety(Q[head+1],Q[head])<=2*sum[i]*getx(Q[head+1],Q[head]))head++; 22 dp[i]=dp[Q[head]]+(sum[i]-sum[Q[head]])*(sum[i]-sum[Q[head]])+m; 23 while(head<tail&&check(i,Q[tail],Q[tail-1]))tail--; 24 Q[++tail]=i; 25 } 26 printf("%d\n",dp[n]); 27 } 28 return 0; 29 }