01揹包問題--golang的入門解
專案一來就瞎忙… 瞎加班… 碌碌無為… 目標還是閒下來就看看演算法吧…
揹包問題
給定 n 種物品和一個容量為 C 的揹包,物品 i 的重量是 wi,其價值為 vi 。應該如何選擇裝入揹包的物品,使得裝入揹包中的物品的總價值最大?。每種物品只有一件,故對每種物品的選擇為放或不放,故問題也稱之為01揹包
例題,物品分別為({重量,價值}),{5,12}, {4,3},{7,10},{2,3}, {6,6},揹包總容量為15。(這裡要是用體積代替重量總覺得好很多,反正意思就是裝進去的物品重量之和不得大於總容量)
解題最關鍵的一行程式碼dp[i][j]=max(dp[i-1][j],dp[i-1][j-c]+v
如果是人為邏輯的話,先考慮裝不裝得下,要是裝得下,就直接裝,裝不下就需要對比思考,是不是把前面的某件或某些件摳出來然後把自己裝進去會比較好,那些摳出來的條件是他們的容量得大於等於自己,不然摳出來自己也進不去。
下一步,就是把上面的邏輯變成函式表達。
首先,需要把問題分解化小,面對裝不下思考把某些摳出來再裝自己的選擇是不是更好的問題,就分解為,假設當前自己是第i件,,判斷【沒扣出來的價值(揹包容量為j,前i - 1件的價值)】和【扣出能放入自己的容量大小揹包裡放的價值,再把自己放進去的價值和(揹包容量為j - c的價值 + v)】二者哪個更大。
我們維護一個二維陣列,x軸為1到15的揹包容量,y軸為依次放入的物品,值為當前揹包的最大價值。
第一次y=1,放入物品{5, 12},x小於5的揹包都放不下,對應值都為0,x大於等於5的揹包都放得下,對應值都為12。
第二次y=2,放入{4, 3}, 相當於現在有兩個物品,重量分別為5、4,x小於4時,都裝不下,值對應都為0,5<=x < 9時,能放入4,也能放入5,值取較大的,值為12。x >=9時,二者皆能放入,值取12+3=15。好,這個規律如何找呢[腦殼痛]。每個x,即揹包大小固定,求值為,上一次對應的價值,和,(x-自己重量)的揹包的上一次能裝的東西+自己價值,取最大值。則為當前揹包大小下能放入的最大值了。
先列表格
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
{5, 12} | 0 | 0 | 0 | 0 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 |
{4, 3} | 0 | 0 | 0 | 3 | 12 | 12 | 12 | 12 | 15 | 15 | 15 | 15 | 15 | 15 | 15 |
{7, 10} | 0 | 0 | 0 | 3 | 12 | 12 | 12 | 12 | 15 | 15 | 15 | 22 | 22 | 22 | 22 |
{2, 3} | 0 | 3 | 3 | 3 | 12 | 12 | 15 | 15 | 15 | 15 | 18 | 22 | 22 | 25 | 25 |
{6, 6} | 0 | 3 | 3 | 3 | 12 | 12 | 15 | 15 | 15 | 15 | 18 | 22 | 22 | 25 | 25 |
再接著看第三次y=3, 放入{7,10},按照上面的公式,max([y-1][x], [y-1][x-c] + v), x=4時,值為max(3, 0), x=5時,值為max(12, 3)…
差不多就是這樣,最後看一下程式碼吧
func backpack(nums [][]int, total int) int {
dp := make([][]int, len(nums))
//初始化二維陣列
for i := 0; i < len(nums); i++ {
dp[i] = make([]int, total+1)
}
//放入第一個物品,填第一行列表
for i:= nums[0][0]; i < total; i++ {
dp[0][i] = nums[0][1]
}
for i := 1; i < len(nums); i++ {
for j:= nums[i][0]; j < total; j++ {
dp[i][j] = max(dp[i-1][j], dp[i-1][j-nums[i][0]] + nums[i][1])
}
}
return dp[len(nums) - 1][total]
}