一、什么是動態規划
動態規划(dynamic programming)是運籌學的一個分支,是求解決策過程(decision process)最優化的數學方法。把多階段過程轉化為一系列單階段問題,利用各階段之間的關系,逐個求解,創立了解決這類過程優化問題的新方法——動態規划。
使用動態規划特征:
1. 求一個問題的最優解
2. 大問題可以分解為子問題,子問題還有重疊的更小的子問題
3. 整體問題最優解取決於子問題的最優解(狀態轉移方程)
4. 從上往下分析問題,從下往上解決問題
5. 討論底層的邊界問題
動態規划最重要的有三個概念:1、最優子結構 2、邊界 3、狀態轉移方程
二、走樓梯問題
有十個台階,從上往下走,一次只能走一個或兩個台階,請問總共有多少種走法?
1、最優子結構:我們來考慮要走到第十個台階的最后一步,最后一步必須走到第八或者第九。不難得到 f(10) = f(9)+f(8)。f(9) = f(8)+f(7)
2、邊界:f(1) = 1, f(2) = 2
3、狀態轉移:f(n) = f(n-1) + f(n-2)
解法一:遞歸
def get_count(n): if n == 1:return 1 if n == 2:return 2 else: return get_count(n-1)+get_count(n-2) print(get_count(10))
程序簡單,但是非常暴力!
程序復雜度分析:
很顯然這是一個滿二叉樹,高度為N-1。所以總節點數為2^N-1,時間復雜度為O(2^N) 。看着就恐怖。
解法二、備忘錄算法
回顧一下上面的遞歸計算方法,我們不難看出,本來總共只有f(1)-f(N)個節點,硬生生被增加到2^N-1個,這就是產生了大量重復的運算。找到問題的根源,對應的解決方法就應運而生,那就是從下往上算,把以前計算過的數值,保存在一個哈希表中,然后后面計算時先查詢一下,存在就無需計算。時間復雜度為O(n) ,空間復雜度為O(n)。但是在仔細一想其實,無需保存所有的f,每個f都只與前兩個值相關,所以空間復雜度可以降低為O(1).我們來看看相關代碼。
def get_count(n): if n == 1:return 1 elif n == 2 :return 2 else: l = [1,2] for i in range(3,n): l[0],l[1] = l[1],l[0]+l[1] return l[0]+l[1]
三、整數拆分
def func(n): l = [1] for i in range(3,n+1): m = 0 for j in range(1,i//2+1): m = max(m,j*(i-j),j*l[i-j-2]) l.append(m) return l print(func(10))
四、最大子序和
核心思想:記錄以前一個數結尾的最長子序列的最大值。
def max_subarry(nums): m = nums[0] tem_m = nums[0] pre = nums[0] for i in range(1,len(nums)): if pre<=0: tem_m = nums[i] pre = nums[i] else: pre+=nums[i] tem_m+=nums[i] if tem_m>m: m = tem_m return m