1. 程式人生 > >淺談動態規劃

淺談動態規劃

重點 alt clas 裏的 cpu 遞推關系 行存儲 val 填充

1 動態規劃的概念: 把問題轉變成狀態(計算機的本質就是一個狀態機,內存裏的各種數據構成了當前的狀態,CPU只能利用當前的狀態去計算下一個狀態),並且將狀態作為緩存進行存儲,當求第 i 個階段的最優解時,可由前 i-1 個階段的最優解得到。動態規劃的方程是:       技術分享圖片 技術分享圖片 2 動態規劃的理解: 先“記憶”之前的某些事情(狀態),在求最終目標(最優狀態)時,直接使用直接緩存的“記憶”。(“記憶”通過遞歸的方式存儲) 3 動態規劃的特點:
  • 最優子結構:當子問題最優時,母問題通過一定的選擇判斷就一定能有最優解的情況。
  • 子問題重疊:當母問題和子問題本質上是一個問題的情況。(子問題之間的參數傳遞是重點!也就是狀態轉移方程)
  • 邊界:當某個子問題不再需要提出子子問題就可以得到答案的情況,這個答案就是邊界。
4 動態規劃和分治的共同點和不同點:
  • 共同點:都是把原問題劃為若幹個子問題,求得子問題的解後,再把子問題的解組合來求得原問題的解。
  • 不同點
    • 分治用遞歸的方式求解子問題的解,重復計算子問題解;動態規劃則用有記憶功能的遞歸式(遞推),提升了效率。
    • 分治的子問題都是獨立的(沒有公共子問題);動態規劃則允許子問題之間有聯系,有交疊。
5 解題思路: 當確定問題具有屬性:當前狀態和之前的狀態有關,即母問題可以由最優子問題得到,且子問題是重疊的。可以確定為動態規劃問題。 如果問題是要維護一張二維表,首先要確定二維矩陣的行和列各代表什麽(背包問題中,行表示物品,列表示包承重) 此時,解題的重點是找到狀態轉移方程。(說白了要就是遞推關系式的確立) 6 背包問題:
問題闡述 n個重量為w1,w2,w3......wn,價值為v1,v2,v3......vn的物品和一個承重為W的背包,求這些物品的最大價值集(能放入背包中)。 問題分析 設F(i,j)為背包問題的最大價值,即:前i個物品放入承重為j的背包中的最大價值。F(i,j)可以看做是一個子問題,他的解可以由之前的子問題的解組合求出。由第i個物品能不能放入承重為j的背包,可以將F(i,j)的解分為兩個類別:包含第i個物品和不包含第i個物品: 當j-wi<0,F(i,j)=F(i-1,j) ps:j-wi<0表示第i個物品放不進承重為j的背包中
當j-wi>=0,F(i,j)=max( F(i-1,j), vi+F(i-1,j-wi) ) ps:F(i-1,j)不等於F(i-1,j-wi) 我們的目標就是求得F(n,W)。可以將這個問題看成是二維表的動態規劃問題,即:回溯構建一個二維表,將其填滿,表的右下角即為問題的解(自底向上的求解方法) 問題求解 采用兩種方法,分別是自底向上的求解,以及帶有記憶的自頂向下求解。
  • 自底向上的動態規劃算法:按子問題從小到大的順序將子問題的解填充到表中,每個子問題都只求解一次。當表填充完畢後,表的右下角的值就是問題的解。
int dp_Backpack(struct backpack bp[n], int W)
{
     int F[n+1][W+1];
     for(int i=0;i<n+1;i++)   
        F[i][0] = 0;
     for(int j=0;j<W+1;j++)   
        F[0][j] = 0;
     for(int i=1;i<n+1;i++)
     {
        for(int j=1;j<W+1;j++)
        {
            if(j<bp[i-1].w)
                F[i][j] = F[i-1][j];
            else
                F[i][j] = max( F[i-1][j], bp[i-1].v+F[i-1][j-bp[i-1].w] );
        }
     }   
     return F[n][W];    
}
  • 帶有記憶的自頂向下動態規劃算法:自頂向下的方式求解,①除表中0行和0列的值為0外,初始化表中所有格為null(可以設為-1);②一旦需要某個格中的值,先檢查該格,若為null,則遞歸調用進行計算並記錄入表,否則直接從表中取值。ps:避免計算不必要的子問題
struct backpack bp[n] = { //初始化bp結構體數組 }
int F[n+1][W+1];
void initF(int F[n+1][W+1])
{
    for(int i=0;i<n+1;i++)    //0列為0
        F[i][0] = 0;
    for(int j=0;j<W+1;j++)    //0行為0
        F[0][j] = 0;
    for(int i=0;i<n+1;i++)    //剩余表中值初始為-1
        for(int j=0;j<W+1;j++)
            F[i][j] = -1;
}
int dp_Backpack(int i, int j)
{
     if(F[i][j] == -1)            //該值需要遞歸計算(在表中從上到下的遞歸計算下來)
     {
         if(j < bp[i-1].w)    
            F[i][j] = dp_Backpack(i-1,j);
         else
            F[i][j] = max( dp_Backpack(i-1,j) , bp[i-1].v+dp_Backpack(i-1,j-bp[i-1].w) );
     }
     return F[i][j];
}
initF(F);
int max_val = dp_Backpack(n,W);
完整代碼 采用以上兩種方法求解01動態規劃問題:
#include <iostream>
using namespace std;
#define N  4                     //物品總數
#define C  5                     //背包的承重
int w[N] = {2,1,3,2};         //物品重量
int v[N] = {12,10,20,15};  //物品價值
int CurWeight = 0;           //當前總重量
int CurValue = 0;              //當前總價值
int x[N] ={0,0,0};               //當前的背包選取情況(1表示選取,0表示不選取)
int MaxValue = 0;             //最大價值
int MaxPack[N] = {0,0,0}; //最大價值下的背包選取情況(1表示選取,0表示不選取)
 
/*用動態規劃解決01背包問題*/
int dp_Backpack_1(int NN, int W)
{
     int n = NN+1;
     int c = W+1;
     int F[n][c];
     for(int i=0;i<n;i++)   
        F[i][0] = 0;
     for(int j=0;j<c;j++)   
        F[0][j] = 0;
     for(int i=1;i<n;i++)
     {
        for(int j=1;j<c;j++)
        {
            if(j<w[i-1])
                F[i][j] = F[i-1][j];
            else
                F[i][j] = max( F[i-1][j], v[i-1]+F[i-1][j-w[i-1]] );
        }
     }   
     return F[NN][W];    
}
 
/*改進的動態規劃求01背包問題*/
void initF(int F[N+1][C+1])
{
    for(int i=0;i<N+1;i++)
        F[i][0] = 0;
    for(int j=0;j<C+1;j++)
        F[0][j] = 0;
    for(int i=1; i<N+1; i++)
        for(int j=1; j<C+1; j++)
            F[i][j] = -1;
}
void printF(int F[N+1][C+1])
{
    for(int i=0; i<N+1; i++)
    {
        for(int j=0; j<C+1; j++)
            cout<<F[i][j]<<" ";
        cout<<endl;
    }
}
int dp_Backpack_2(int F[N+1][C+1], int i, int j)
{
     if(F[i][j] == -1)        //該值需要遞歸計算(在表中從上到下的遞歸計算下來)
     {
         if(j < w[i-1])        
            F[i][j] = dp_Backpack_2(F,i-1,j);
         else
            F[i][j] = max( dp_Backpack_2(F,i-1,j) , v[i-1]+dp_Backpack_2(F,i-1,j-w[i-1]) );
     }
     return F[i][j];
}
 
int main()
{
    cout<<"use dp_1‘s value: "<<dp_Backpack_1(N,C)<<endl;
    int F[N+1][C+1];
    initF(F);
    cout<<"use dp_2‘s value: "<<dp_Backpack_2(F,N,C)<<endl;
    printF(F);
}

淺談動態規劃