動態規劃

動態規劃與分支方法相似,都是通過組合子問題的解求解原問題
其步驟分爲:
(1):刻畫一個最優解的結構特徵。
(2):遞歸地定義最優解的值。
(3):計算最優解的值,通常採取自底向上的方法。
(4):利用計算出的信息構造一個最優解。
## 鋼條切割問題 ##

這裏寫圖片描述

CUT-ROD(int *p,n)
if(n==0)
return 0;
int q=-INF;//作爲標記
for(int i=1;i<=n;i++)
{
int q=max(q,p[i]+CUT-ROD(p,n-i));
}
return q;

過程CUT-ROD以價格數組p[1…n]和整數n作爲輸入,返回長度爲n的鋼條的最大收益,若N=0,不可能有任何收益,所以返回0,
然後將最大收益初始化爲負無窮,下面就是計算結果。。運用遞歸的思想。

## 帶備忘的自頂向下法 ##
此方法按照自然的遞歸形式編寫過程,但過程會保存每個子問題的解。當需要一個子問題的解時,過程首先檢查是否已保存過此解。如果是,則直接返回保存的值,從而節省了計算時間,否則按照通常的方式計算這個問題.所以我們稱這個遞歸過程是帶備忘的,因爲他記住了之前已經計算出的結果。
MEMORIZD-CUT-ROD(int *p,int n)
{
int r[n];
for(int i=0;i<n;i++)
{
r[i]=-INF;
}
return MEMORIZED-CUT-ROD-AUX(p,n,r);
}
MEMORIZED-CUT-ROD-AUX(int *p,int n,int *r)
{
if(r[n]>=0)
    {
        return r[n];
    }
if(n==0)
    int q=0;
else
{
    int q=-INF;
    for(int i=1;i<n;i++)
        {
            q=max(q,p[i]+MEMORIZED-CUT-ROD-AUX(p,n-i,r));
        }
}
    r[n]=q;
    return q;
}

僞代碼解釋
這裏寫圖片描述

## 自底向上版本 ##
BOTTOM-UP-CUT-ROD(int *p,int n)
{
int r[n]//new array
r[0]=0;
for(int j=0;j<n;j++)
{
    int q=-INF;
    for(int i=1;i<=j;i++)
    {
    q=max(q,p[i]+r[j-i])
    }
r[j]=q;
}
return r[n];
}
## 僞代碼解釋(自底向上) ##

創建一個新數組r[]來保存子問題的解,將其第一個元素初始化爲0;因爲長度爲0的鋼條沒有收益。接下來,解釋按照升序求解每個規模爲J的子問題,方法同上,只是現在直接訪問數組元素r[j-i]來獲得規模爲j-i的子問題的解,而不必進行遞歸調用,然後將規模爲J的子問題存入r[j]。最後返回r[n],即最優解r_n;

## 重構解 ##

前文給出的鋼條切割問題的動態規劃返回最優解的收益值,但並未返回解本身,我們可以擴展動態規劃算法,使之對每個子問題不僅保存最優收益值,還能保存對應的切割方案
於是下面代碼的作用是得到最大收益值,還能得到對應的第一段鋼條的切割產長度。

EXTENDED-BOTTOM-UP-DUT-ROD(int *p,int n)
{
int r[n];
int s[n];
r[0]=0;
for(int j=1;j<n;j++)
 {
q=-INF;
for(int i=1;i<j;i++)
{
 if(q<p[i]+r[j-i])
   {
  q=p[i]+r[j-i];
  s[j]=i;
   }
}
 r[j]=q;
 }
 return r && s;
}

下面的過程接受兩個參數:價格表P和鋼條長度N然後調用上面的函數來計算切割下來的每段鋼條的長度S[1…N],然後輸出長度爲N的鋼條的完整的最優切割方案:

PEINT-CUT-ROD-SOLUTION(int *p,int n)
{
(r,s)=EXTENDED-BOTTOM-UP-CUT-ROD(p, n);
while(n>0)
{
printf s[n];
n=n-s[n];
}
}

程序結果:
這裏寫圖片描述

發佈了72 篇原創文章 · 獲贊 4 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章