有趣的動態規划(golang版本)


多年前就聽過這個動態規划,最近在復習常用算法的時候才認真學習了一下,發現蠻有意思,和大家安利一波。

定義:

准確來說,動態規划師吧一個復雜問題分解成若干個子問題,並且尋找最優子問題的一種思想,而不是一種特定的算法。

聽上去和我們常用的遞歸有點類似,但是注意:其中子問題的解被重復使用。也就是利用這個特性,我們可以把一個復雜的問題抽象轉換成一個簡單二維表來進行推演。

動態規划的解題關鍵在於:

  • 1.根據問題可能性進行拆分。
    從最簡單的情況下進行分析,從下往上逐步分析。
  • 2.找到狀態轉移方程式,保存最優解。
    方程式其實就是在滿足某個條件下的遞推通項公式,同時也要注意條件范圍和邊界處理。

最有名的是背包問題:將N種類型的物件放到一個容量為M的背包里面,尋找最優解。

一般來說可以用暴力枚舉的方式去算近似最優,但是從空間復雜度和時間復雜度來看使用動態規划更好,因為每一步的結果會保存下來給下一步計算使用,節約了不少時間消耗,最終算法性能極高。

下面舉一個典型的例子,來自牛客網的一道"湊整題":

給你六種面額 1、5、10、20、50、100 元的紙幣,假設每種幣值的數量都足夠多,編寫程序求組成N元(N為0~10000的非負整數)的不同組合的個數。

仔細分析后發現,這是一個用不同類型的面額組合拼湊固定金額的組合最優解問題。由於N為0 ~ 10000的非負數,我們可以假設N取10來分析。
分析結果如圖:

image

偽代碼如下:

if (j - price[i] >= 0) {
    Fn(amount) = Fn(j - price[i]) + Fn-1(amount);
} else {
    Fn(amount) = Fn-1(amount);
}

其中的Fn函數可以用一個二維數組來實現,第二維為面額個數(即n),第一維度為amount+1種,用於對應的組合數。

用golang實現如下:

func getTheNum(num int) int {
    var dp [5][10000]int
    moneys = int[...] {1, 5, 10 ,20 , 50, 100}// 面額數組
    for i := 0; i < 5; i++ { // 用前i種面額拼湊1rmb的方法數均為1
        dp[i][0] = 1
    }
    for i := 0; i <= num; i++ { // 用1rmb面額拼湊0金額的方法數都為1
        dp[0][i] = 1
    }
    
    for i := 1; i < 5; i++ { // 每一種面額組合遞推
        for j := 1; j <= num; j++ {
            if j - moneys >= 0 { // 當當前金額大於這次循環的面額值,則組合數等於當前i種面額拼湊j金額的組合數+前i+1種面額拼湊j - moneys[i]金額的組合數
                dp[i][j] = dp[i-1][j] + dp[i][j - moneys[i]]
            } else {
                dp[i][j] = dp[i-1][j]
            }
        }
    }
    
    return dp[4][num] // 返回最后一項
}

還可以進一步簡化,因為二維數組保存的結果在每一次循環判斷中都被保存下來了,所以用一維數組也可以保留。改進實現如下:

func SimpleGetNum(num int) int {
    var dp [10000]int
    moneys = int[...] {1, 5, 10 ,20 , 50, 100}// 面額數組
    for i := 0; i <= num; i++ { // 用1rmb面額拼湊0金額的方法數都為1
        dp[i] = 1
    }
    
    for j := 1; j <= 5; j++ {
		for i := 1; i <= num; i++ {
			if i >= moneys[j] { // 當前面金額大於面額的時候需要計算前i種面額組合出i - moneys[j]的方法數
				dp[i] += dp[i - moneys[j]]
			}
		}
	}

	return dp[num]
}

探索的過程就如同RPG游戲,算法真的是一個很有趣的事情,很多都像精巧的數學問題的代碼化。


免責聲明!

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



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