動態規劃之線性動規鋼條切割問題

問題描述

線性規劃是一類問題。目標函數爲特定變量的線性函數,約束是這些變量的線性不等式(standard form)或等式(slack form),目的是求目標函數的最大值或最小值。這類動態規劃是平時比較常見的一類動態規劃問題。

假設公司出售一段長度爲i英寸的鋼條的價格爲Pi(i = 1, 2, …單位:美元),下面給出了價格表樣例:

這裏寫圖片描述
切割鋼條的問題是這樣的:給定一段長度爲n英寸的鋼條和一個價格表Pi,求切割方案,使得銷售收益Rn最大。
當然,如果長度爲n英寸的鋼條價格Pn足夠大,最優解可能就是完全不需要切割。

解法

我們可以把他分解成兩個子問題,然後兩個子問題的最優解相加,再比較所有相加的和裏選出最優的。每個子問題也可以往下繼續分成兩個子問題,層層分下去。比如我現在長度爲5,那麼就可以分成1+4、2+3。1+4裏面的4又可以劃分子問題,分成1+3、2+2、依此循環下去求出每個子問題的最優解後,與原來本數字的價格比較,誰更大要誰,那麼總問題也就解決了
動規表達式:
max[n]=price[n]; (n=0)
max[n]=max(price[n],Ri+Rn-i) (n>0)
注:R代表大利潤

使用遞歸實現

     static int DP(int n){
         int temp=0;
         if(n==0){
             return price[n];
         }

         for(int i=0;i<=(n/2);i++){

             temp=DP(i)+DP(n-i-1);
             max[n]=max(price[n],temp); 
         }
         return max[n];
     }

測試本頁數據通過,測試4的時候,for循環跑了8次。感覺效率很低,很多重複子問題都進行了求解,所以改進一下本算法,存儲子問題的解,每次進行檢測,讓重複子問題不再進行二次求解,也就是我們說的動態規劃思想。犧牲一定的空間,換取時間。

帶備忘錄的自上而下求法。

 static int DP(int n){
         int temp=0;
         if(r[n]>=0){
             return r[n];
         }
         if(n==0){
             return price[n];
         }

         for(int i=0;i<=(n/2);i++){

             temp=DP(i)+DP(n-i-1);
             max[n]=max(price[n],temp);
         }
         r[n]=max[n];
         return max[n];

測試長度爲4時循環只跑了五次,大大提高了效率。

本題另外一種寫法
動規表達式:
Rn=max(Pi+Rn-i)

int Memorized_cut_rod_Aux(int p[], int n, int r[])  
{  
    int q;  
    int i;  
    if (r[n]>=0)//是否子問題重複 
    {  
        return r[n];  
    }  

    if (n==0)  
    {  
        q=0;  
    }  
    else  
    {  

        q=-1;  
        for (i=1; i<=n; i++)  
        {  

            q=max(q,p[i]+Memorized_cut_rod_Aux(p,n-i,r));  
        }  
    }  
    r[n]=q;  
    return q;  
}  

由以上鋼條切割問題我們發現,動態規劃有時並不是必須的,但是使用常規方法效率極其低下。而動態規劃之所以能提高程序的效率,是因爲我們避免了重複子問題的求解。我們使用動態規劃總是在子子問題的基礎上求解子問題,再由子問題出求解最終的答案,而這一切的一切關鍵在於子問題的建立。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章