灯神动态规划(Dynamic Programing)学习笔记 打劫问题 凑整问题 背包问题 例题+原理+源码超详细讲解
動態規劃Dynamic Programing學習筆記 打劫問題 湊整問題 背包問題
- 學習資源
- Example1. 打家劫舍問題 looting problem
- Example2. 湊整問題
- Example3. 背包問題
學習資源
燈神的視頻是我目前個人覺得講解最清晰的動態規劃教學,這篇文章是對他視頻內容的匯總,在此基礎上我還個人補充了背包問題的解決方案。
燈神的b站小課堂
Example1. 打家劫舍問題 looting problem
題目模型簡化如下: 給定數組arr=[4,1,1,9,1],從中選擇數字來使得總和最大,注意不能選擇相鄰的數字。例如,選擇4后,不能選擇1;選擇9后,不能選擇9左右兩側的數字1。
模型建立。我們拿出一組新的數組arr=[1,2,4,1,7,8,3],將其按順序分別編號為0-6。如圖
假定opt(6)表示:從前6個數字中選出總和最大的最佳方案(也就是我們期望得到的結果),同理opt(5)表示從前五個數字中選出來總和最大的方案……依次類推。
我們在這個基礎上進行推理:對于第六個數字,我們有選擇和不選擇這兩種操作,當我們選擇第六個數時,根據規則就無法選擇第五個數字,因此opt(6)=opt(4)+arr[6],即在這種情況下,opt6的值等于前四個數字中選出的最大值加上第六個數的數值。如果我們不選擇第六個數,那么opt(6)=opt(5),即前五個數所能選出總和最大的值。
根據這個邏輯,為了算出opt6,我們需要經歷以下過程。加號表示選擇當前數,減號表示不選擇當前數。整個過程的樹狀圖如下根據上圖所示的邏輯,我們可以從中推出普遍的遞推規律。因為我們是求最大值,所以前i個數字所能構成的最大總和為opt(i)=max(opt(i-1),opt(i-2)+arr[i]),其中,opt(i)是我們放棄選擇第i個數時所得到的前i-1個數能構成的最大值;opt(i-2)+arr[i] 是我們選擇第i個數時能得到的最大值。
尋找出口條件:#當i=0時 opt(1)=max(arr[1],arr[0]) #當i=1時 opt(0)=arr(0)
用代碼來實現當前邏輯。分兩種,首先是recursive method
運行這段代碼后可以發現最終結果為15。這個地方我們運行的基本原理就是在函數中不停地調用自身,最終由opt6出發推演至opt0,直到算出答案(如上面我手繪的樹狀圖)。這種辦法雖然變成起來思路比較直觀,但在該推演過程中有很多重復的運算。回到上面的樹狀圖,該辦法在計算opt6時,重復計算了opt4和opt3,我分別用黃色、綠色熒光筆將重復部分標記了起來。
當arr內數據和i的數量變大時,使用這種辦法的運算成本將大幅升高。接下來介紹非recursive的計算方法:
#續上面的代碼 def nonrec_method(arr):opt = np.zeros(len(arr)) #建立數組來存儲opt數據opt[0]=arr[0]opt[1]=max(arr[1],arr[0])for i in range(2,len(arr)):A = opt[i-2]+arr[i]B = opt[i-1]opt[i] = max(A,B)return opt[len(arr)-1] nonrec_method(arr)輸出結果為15.0。該種方法可以理解為逆著樹狀圖,由opt0出發一步一步推演至最終結果opt6,好處在于每一步的結果我們都儲存在了數組中,節約了不少計算資源。
Example2. 湊整問題
題目要求如下,給定數組arr=[3,34,4,12,5,2],判斷能否實用數組內的數構成S(s為一個數)。
模型建立:假定S=9,將arr中的數據從0-5進行編號。如圖
我們設subset(i,S),其中i表示使用前i個數字,S為我們期望拼湊成的數。例如,subset(5,9)表示使用前五個數字來拼湊出9的總和。subset(2,3)表示使用前兩個數字拼湊出3。我們從這個符號出發,開始推理過程,首先考慮subset(5,9),我們此時要用前五個數來拼出9,首先判斷第五個數。在這個地方我們有兩種選擇,第一種選擇是使用第五個數來拼湊出9,那么選擇后的情況就變成了subset(4,7),表示用前4個數去湊成7。第二種選擇是不使用第五個數,那么情況就變成了subset(4,9)。繪制成樹狀圖如下
由此我們可以推出普遍的遞推規律,若使用前i個數湊成S,遞推規律表達式為其中arr表示存放數據的數組。
接下來,我們開始尋找出口條件,第一種情況是當S下降為0時,說明前面的數字已經完成了拼湊任務,此時應該返回True,表示能夠拼湊出S;第二種情況是當i下降為0時,S仍然不為0。i=0說明已經引導到了數組的首位數,如果此時滿足arr[0]==S,那么說明能夠湊出S,返回True。如果不相等則說明arr中的數字無法拼成S,返回False。這兩個出口條件總結如下:
運行后結果為True,說明arr內的數字能夠湊出9。
運行后輸出結果同樣為True,但該方法中,我們使用numpy創建了一個數組,依次計算并存儲了各個階段中subset的bool值,避免了前面recursive method中出現的重復計算的問題。本質上,這種辦法相當于創建了一個如下的bool表格:紅圈就是我們最終想要得到的結果,即`subset(5,9)
Example3. 背包問題
總結
以上是生活随笔為你收集整理的灯神动态规划(Dynamic Programing)学习笔记 打劫问题 凑整问题 背包问题 例题+原理+源码超详细讲解的全部內容,希望文章能夠幫你解決所遇到的問題。