[數據結構][Python][經典題目][動態規劃][經典01揹包]求解金礦問題

題目:

有一個國家發現了5座金礦,每座金礦的黃金儲量不同,需要參與挖掘的工人數也不同。參與挖礦工人的總數是10人。
每座金礦要麼全挖,要麼不挖,不能派出一半人挖取一半金礦。要求用程序求解出,要想得到儘可能多的黃金,應該選擇挖取哪幾座金礦?

金礦編號 黃金儲量 需要人數
1 500 5
2 200 3
3 300 4
4 350 3
5 400 5

思路:

1、首先,對於問題中的金礦,每一個金礦都存在這挖和不挖的選擇
假設:最後一個金礦不被挖掘,那麼問題簡化爲10個人在前4個金礦中做出最優選擇:
在這裏插入圖片描述
相應的,假設最後一個金礦一定會被挖掘,問題轉換成以下:由於最一個金礦消耗了3個工人,問題轉換成7個工人在前4個金礦中做出最優選擇。
在這裏插入圖片描述
這兩種簡化情況,被稱爲全局問題的兩個最優子結構。

動態規劃要點

確定全局最優解和最優子結構之間的關係,以及問題的邊界,即狀態轉移方程。
以上,可以吧金礦數量設爲n,工人數量設置爲w,金礦的含金量設爲數組g[],金礦所需開採人數設爲數組p[],設F(n,w)爲n個金礦,w個工人時的最優收益函數,那麼狀態轉移方程爲F(n,w)(n=0或w=0)
問題的邊界爲金礦數量爲0或者工人數量爲0的情況。
F(n,w)=F(n-1,w)(n>=1,w<p[n-1])
所剩的工人不夠挖掘當時的金礦時,只有一種最優子結構。
F(n,w)=Max(F(n-1,w),F(n-1,w-p[n-1])+g[n-1])(n>=1,w>=p[n-1])
在常規的情況下,具有兩種最優子結構(挖當前的金礦或者不挖當前的金礦)。

求解過程

在這裏插入圖片描述
表格最左側代表不同的金礦選擇範圍,從上到下,每多增加行,就代表多1個金礦可供選擇,也就是F(n,w)函數中的n值
表格的最上方代表工人的數量,從1個工人到10個工人,也就是F(n,w)函數的w值
下面我們從第1行第1列開始,嘗試把空白的格子一一填滿,填充的依據是狀態轉移方程。
對於第一行的前四格子,由於w<p[n-1],對應的狀態轉移方程爲:
F(n,w)=F(n-1,w)(n>1,w<p[n-1])
帶入求解:
F(1,1)=F(1-1,1)=F(0,1)=0
F(1,2)=F(1-1,2)=F(0,2)=0
F(1,3)=F(1-1,3)=F(0,3)=0
F(1,4)=F(1-1,4)=F(0,4)=0
在這裏插入圖片描述
第1行的後6個格子:此時w>=p[n-1],對於如下公式:
F(n,w)=Max(F(n-1,w),F(n,w-p[n-1])+g[n-1])(n>1,w>=p[n-1])
帶入求解:
F(1,5)=Max(F(1-1,5),F(1-1,5-5)+400)=Max(F(0,5),400))=400

F(1,10)=Max(F(1-1,10),F(1-1,10-5)+400)=Max(F(0,5),400))=400
在這裏插入圖片描述

在這裏插入圖片描述

優化

4個金礦、9個工人的最優結果,是由他的兩個最優子結構,也就是3個金礦、5個金礦和3個金礦、9個工人的結果推導出來。這兩個最優子結構都位於他的上一行。
所以,在程序中並不需要保持整個表格,無論金礦有多少座,我們只要保存1行的數據即可。在計算下一行的時候,要從右向左統計,把舊數據一個一個替換掉。

代碼


def getGoldMing(w,p,g):
    result = [0 for x in range(w+1)]
    for i in range(1,len(g)+1):
        for j in range(w,-1,-1):
            if j>=p[i-1]:
                result[j]=max(result[j],result[j-p[i-1]]+ g[i-1])

    return result[w]

if __name__ == '__main__':
    w = 10
    p = [5, 5, 3, 4, 3]
    g = [400, 500, 200, 300, 350]
    res = getGoldMing(w,p,g)
    print(res)

在這裏插入圖片描述

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