1. 程式人生 > >HDU 3507 Print Article(斜率優化DP)

HDU 3507 Print Article(斜率優化DP)

hdu clas 進行 維護 直接 元素 string 單調性 ons

題目鏈接: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)