HDU 3507 Print Article(斜率优化DP)

题目链接: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 }

 

posted @ 2017-11-07 22:58  Yeader  阅读(146)  评论(0编辑  收藏  举报