30分鐘弄懂動態規劃算法詳細講解(超詳細)

動態規劃對於很多人來說是一道過不去的坎,因爲很多的教程或者書籍都講得太抽象,讀者看了都雲裏霧裏

其實動態規劃是很簡單的,今天,我就來講講動態規劃是怎麼實現的.

一 動態規劃作用:

動態規劃一般是來解決

1計數

2求最大值,最小值

3求存在性

二 動態規劃怎麼用(四部曲):

1.確定狀態(兩個核心:1最後一步 2化成子問題)

2轉移方程

3開始和邊界條件

4計算順序

這麼一說,太抽象了,這四部曲是什麼鬼東西(黑人問號.jpg)???接下來先用例子分析分析

問題:(lintcode:669)

首先,看到這種題,先不考慮算法,直接憑直覺的想法是這樣的

7元*3+5元*1+2元*1=28 (呃,好像不能這樣),再這樣

7元*3+2元*3=27 ,一共6枚硬幣(這樣總可以了吧)

其實,正確答案是7元*1+5元*4=27 ,5枚硬幣(震驚!!!!!!)

接下來用動態規劃來(四部曲)分析:

1.確定狀態

   1.1"最後一步"

   首先我們把最後一個硬幣的面值是ak,那麼前面的硬幣值肯定是27-ak

接下下,"最後一步"還有兩個注意的關鍵點

  1.2 化解子問題

還有個問題,我們還不知ak是多少,所以需要

這樣我們的第一步就完成了

2 確定轉移方程

  根據第一步的狀態,我們這樣設置,(注意,上面的大括號在這一步已經變成方括號了)

 

完成這一步可以先喝杯咖啡了

3.確定開始和邊界條件

 首先我們的硬幣肯定從f[0]即湊出0元需要0枚硬幣,

接下來是邊界問題:

 

4計算順序

有了我們前面分析的前三步,接下來就是計算順序了,其實就是從f[0]開始計算而已,並把他們都記錄下來

結果其實是這樣的

 

小結:

 

說了這麼多,沒代碼啊,無代碼無真相!!接下來上代碼,

 

/**
 * 
 * @param {Array} coin 代表硬幣的種類 [2,5,7]
 * @param {int} M 代表拼湊的值  27
 */
function coinChange(coin, M) {
    let f = []
    f[0] = 0 //初始化第一步
    for (let i = 1; i <= M; i++) {
        f[i] = Number.MAX_VALUE //開始默認無窮大,即不能拼湊

        for (let j = 0; j < coin.length; j++) {  //這裏右三種面值的硬幣,需要循環一次
            if (i - coin[j] >= 0 && f[i - coin[j]] != Number.MAX_VALUE) {//小於0即不能拼湊,和無窮大+1會報錯
                f[i] = Math.min(f[i], f[i - coin[j]] + 1)
            }
        }
    }
    if (f[M] == Number.MAX_VALUE) {
        return -1
    }
    return f[M] //數組最後一個值就是我們想要的結果
}

 

接下來,繼續看計數型的動態規劃(lintcode:114)

解答這個問題,還是四部曲:

1.確定狀態

     1.1 "最後一步"

  1.2化成子問題

2.  確定轉移方程

 

3.確定開始和邊界條件

4計算順序

分析完了,上代碼

 

/**
 * 
 * @param {int} m m行
 * @param {int} n n列
 */
/**
 * @param m: positive integer (1 <= m <= 100)
 * @param n: positive integer (1 <= n <= 100)
 * @return: An integer
 */
const uniquePaths = function (m, n) {
    let f=[];//先聲明一個一維數組的
    for(let i=0;i<m;++i){
        f[i]=[];//聲明二維數組的,其他語言有其他的寫法
        for(let j=0;j<n;++j){
            if(i===0||j===0){
                f[i][j]=1;
            }else{
                f[i][j]=f[i-1][j]+f[i][j-1];
            }
        }
    }
    console.log(f)
    return f[m-1][n-1];
};

 

 

接下來繼續分析一道題(lintcode:116):

1.確定狀態

   1.1"最後一步"

   1.2 化爲子問題

2. 確定轉移方程

3.確定開始和邊界條件

4. 計算順序

最後,上代碼

/**
 * 
 * @param {Array} a //代表每個石頭能跳的長度 
 */
function canJump(a){
    let n=a.length //數組的長度爲能跳的總石頭數
    let f=[]
    f[0]=true
    for(let j=1;j<n;j++){
        f[j]=false
        for(let i=0;i<j;i++){
            if(f[i]&&a[i]+i>=j){
                f[j]=true
                break
            }
        }
    }
    return f[n-1]
}

現在,動態規劃基本完成入門了,都是四部曲.

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