1. 程式人生 > >01揹包問題--golang的入門解

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

,c代表第i個物件的重量,v表達第i個物件的價值。說是理解了這行程式碼這道題就會了。

如果是人為邏輯的話,先考慮裝不裝得下,要是裝得下,就直接裝,裝不下就需要對比思考,是不是把前面的某件或某些件摳出來然後把自己裝進去會比較好,那些摳出來的條件是他們的容量得大於等於自己,不然摳出來自己也進不去。

下一步,就是把上面的邏輯變成函式表達。

首先,需要把問題分解化小,面對裝不下思考把某些摳出來再裝自己的選擇是不是更好的問題,就分解為,假設當前自己是第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]
}