标签:复杂 直接 个数 space turn 参考 ref print www.
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3507
题目大意:概题意就是要输出N个数字a[N],输出的时候可以连续连续的输出,每连续输出一串,它的费用是 “这串数字和的平方加上一个常数M”。(N<=500000,M<=1000)
解题思路:参考了这里的思路,这算是我写得第一题斜率优化DP了,当做模板来用吧。推理下次补上。
我们设dp[i]表示输出到i的时候最少的花费,sum[i]表示从a[1]到a[i]的数字和。于是状态转移方程就是:
dp[i]=min(dp[i],dp[j]+M+(sum[i]-sum[j])^2)(0<=j<i)
但是题目给出的N为最大500000,这个O(n^2)的算法显然会超时,于是可以用斜率优化,利用队列维护单调性进行优化,将复杂度降至O(n)。
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int N=5e5+5; 7 8 int n,m,head,tail; 9 int dp[N],sum[N],q[N];//q为需要维护的队列 10 11 // dp[i]= min{ dp[j]+M+(sum[i]-sum[j])^2 } 12 int getDP(int i,int j){ 13 return dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]); 14 } 15 16 //yj-yk 17 int getUP(int j,int k){ 18 return dp[j]+sum[j]*sum[j]-(dp[k]+sum[k]*sum[k]); 19 } 20 21 //xj-xk 22 int getDOWN(int j,int k){ 23 return 2*(sum[j]-sum[k]); 24 } 25 26 int main(){ 27 while(~scanf("%d%d",&n,&m)){ 28 dp[0]=sum[0]=0; 29 for(int i=1;i<=n;i++){ 30 scanf("%d",&sum[i]); 31 sum[i]+=sum[i-1]; 32 } 33 head=tail=0; 34 q[tail++]=0; 35 for(int i=1;i<=n;i++){ 36 //队列元素有两个以上时,维护g(k,j)=(yj-yk/xj-xk)<sum[i],注意分母为0不能直接比较斜率 37 while(head+1<tail&&!(getUP(q[head],q[head+1])<sum[i]*getDOWN(q[head],q[head+1]))){ 38 head++;//相当于淘汰q[head]也就是k 39 } 40 dp[i]=getDP(i,q[head]); 41 //队列元素有两个以上时,维护g(k,j)<g(j,i) 42 while(head+1<tail&&!(getUP(q[tail-1],i)*getDOWN(q[tail-2],q[tail-1])>getUP(q[tail-2],q[tail-1])*getDOWN(q[tail-1],i))){ 43 tail--; 44 } 45 q[tail++]=i; 46 } 47 printf("%d\n",dp[n]); 48 } 49 return 0; 50 }
HDU 3507 Print Article(斜率优化DP)
标签:复杂 直接 个数 space turn 参考 ref print www.
原文地址:http://www.cnblogs.com/fu3638/p/7801832.html