HDU 3507 Print Article(斜率DP優化)


Print Article

Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 131072/65536 K (Java/Others)
Total Submission(s): 2224    Accepted Submission(s): 728


Problem Description
Zero has an old printer that doesn't work well sometimes. As it is antique, he still like to use it to print articles. But it is too old to work for a long time and it will certainly wear and tear, so Zero use a cost to evaluate this degree.
One day Zero want to print an article which has N words, and each word i has a cost Ci to be printed. Also, Zero know that print k words in one line will cost

M is a const number.
Now Zero want to know the minimum cost in order to arrange the article perfectly.
 

 

Input
There are many test cases. For each test case, There are two numbers N and M in the first line (0 ≤ n ≤ 500000, 0 ≤ M ≤ 1000). Then, there are N numbers in the next 2 to N + 1 lines. Input are terminated by EOF.
 

 

Output
A single number, meaning the mininum cost to print the article.
 

 

Sample Input
5 5 5 9 5 7 5
 

 

Sample Output
230
 

 

Author
Xnozero
 

 

Source
 

 

Recommend
zhengfeng
 
 
 
 

此題是很基礎的斜率DP的入門題。

題意很清楚,就是輸出序列a[n],每連續輸出的費用是連續輸出的數字和的平方加上常數M

讓我們求這個費用的最小值。

設dp[i]表示輸出前i個的最小費用,那么有如下的DP方程:

dp[i]= min{ dp[j]+(sum[i]-sum[j])^2 +M }  0<j<i

其中 sum[i]表示數字的前i項和。

相信都能理解上面的方程。

直接求解上面的方程的話復雜度是O(n^2)

對於500000的規模顯然是超時的。下面講解下如何用斜率優化DP使得復雜度降低一維。

 

我們首先假設在算 dp[i]時,k<j ,j點比k點優。

也就是

dp[j]+(sum[i]-sum[j])^2+M <= dp[k]+(sum[i]-sum[k])^2+M;

所謂j比k優就是DP方程里面的值更小

對上述方程進行整理很容易得到:

[(dp[j]+sum[j]*sum[j])-(dp[k]+sum[k]*sum[k])] / 2(sum[j]-sum[k]) <=sum[i].

注意整理中要考慮下正負,涉及到不等號的方向。

左邊我們發現如果令:yj=dp[j]+sum[j]*sum[j]   xj=2*sum[j]

那么就變成了斜率表達式:(yj-yk)/(xj-xk) <= sum[i];

而且不等式右邊是遞增的。

所以我們可以看出以下兩點:我們令g[k,j]=(yj-yk)/(xj-xk)

第一:如果上面的不等式成立,那就說j比k優,而且隨着i的增大上述不等式一定是成立的,也就是對i以后算DP值時,j都比k優。那么k就是可以淘汰的。

第二:如果 k<j<i   而且 g[k,j]>g[j,i] 那么 j 是可以淘汰的。

假設  g[j,i]<sum[i]就是i比j優,那么j沒有存在的價值

相反如果 g[j,i]>sum[i] 那么同樣有 g[k,j]>sum[i]  那么 k比 j優 那么  j 是可以淘汰的

 

所以這樣相當於在維護一個下凸的圖形,斜率在逐漸增大。

通過一個隊列來維護。

 

 

/*
HDU 3507

*/

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
const int MAXN=500010;

int dp[MAXN];
int q[MAXN];//隊列
int sum[MAXN];

int head,tail,n,m;
// dp[i]= min{ dp[j]+M+(sum[i]-sum[j])^2 };
int getDP(int i,int j)
{
    return dp[j]+m+(sum[i]-sum[j])*(sum[i]-sum[j]);
}

int getUP(int j,int k) //yj-yk部分
{
    return dp[j]+sum[j]*sum[j]-(dp[k]+sum[k]*sum[k]);
}
int getDOWN(int j,int  k)
{
    return 2*(sum[j]-sum[k]);
}

int main()
{
  //  freopen("in.txt","r",stdin);
  //  freopen("out.txt","w",stdout);
    while(scanf("%d%d",&n,&m)==2)
    {
        for(int i=1;i<=n;i++)
           scanf("%d",&sum[i]);
        sum[0]=dp[0]=0;
        for(int i=1;i<=n;i++)
           sum[i]+=sum[i-1];
        head=tail=0;
        q[tail++]=0;
        for(int i=1;i<=n;i++)
        {
            //把斜率轉成相乘,注意順序,否則不等號方向會改變的
            while(head+1<tail &&  getUP(q[head+1],q[head])<=sum[i]*getDOWN(q[head+1],q[head]))
               head++;
            dp[i]=getDP(i,q[head]);
            while(head+1<tail && getUP(i,q[tail-1])*getDOWN(q[tail-1],q[tail-2])<=getUP(q[tail-1],q[tail-2])*getDOWN(i,q[tail-1]))
                    tail--;
            q[tail++]=i;
        }
        printf("%d\n",dp[n]);
    }
    return 0;
}

 

 

初學斜率DP感覺很難,但是理解了原理之后感覺還是比較簡單的。等深入之后再來總結下斜率DP

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM