Danamic Programing : 背包問題
==
>在M件物品裡取出若干件放在負重為W的背包裡,每件物品的重量為W1,W2,W3····Wn,與這些物品對應的價值分別對應為P1,P2,P3·· ···Pn,如何求出這個背包能裝的最大價值?
>
---
## 圖解法
解這類的提問,一般會先用圖解法,畫表格來解
方便起見,先將題目定為 :
$M = 5, W = 16$
$W_1, W_2, W_3, W_4, W_5 = [3, 4, 7, 8, 9]$
$P_1, P_2, P_3, P_4, P_5 = [4, 5, 10, 11, 13]$
|$W/P$| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
|---|---|---|---|---|---|---|---|---|---|---|----|----|----|----|----|----|----|
| 3 | 0 | 0 | 0 | 4 | 4 | 4 | 8 | 8 | 8 | 12 | 12 | 12 | 16 | 16 | 16 | 20 | 20 |
| 4 | 0 | 0 | 0 | 4 | 5 | 5 | 8 | 9 | 10 | 12 | 13 | 14 | 16 | 17 | 18 | 20 | 21 |
| 7 | 0 |0 |0 |4 |5| 5| 8| 10| 10| 12| 14| 15| 16| 18| 20| 20| 22 |
| 8 | 0 |0 |0 |4 |5| 5| 8| 10| 11| 12| 14| 15| 16| 18| 20| 21| 22 |
| 9 | 0| 0| 0| 4| 5| 5| 8| 10| 11| 13| 14| 15| 17| 18| 20| 21| 23 |
由表中可以得出:當揹包負重`W = 16`時,背包裡物品的最大價值`P = 23`
---
## 程式解法
以`Golang`為例 :
```go=
func dp(objArr, valueArr []int, target int, compareFunc func(a, b int) int) int {
// memorize data 初始化
var res [][]int
res = append(make([][]int, len(sourceArr)))
for i := 0; i < len(sourceArr); i++ {
res[i] = make([]int, target+1)
}
// 狀態轉換方程
for i := 0; i < len(sourceArr); i++ {
for j := 0; j <= target; j++ {
if i == 0 {
res[i][j] = 0
} else {
if j == 0 {
res[i][j] = 0
} else if objArr[i] > j {
res[i][j] = res[i-1][j]
} else {
res[i][j] =
compareFunc(valueArr[i]+res[i][j-objArr[i]], res[i-1][j])
}
}
}
}
return res[len(sourceArr)-1][target]
}
```
這邊稍微解說一下
主要的邏輯是:
$F(m,n) = Min(P_m + F(m, n - W_m), F(m-1, n))$
意即是在背包負重為`n`公斤且第`m`個物體加進參考的當下,背包裡物體最大的總價值為$F(m,n)$
`compareFunc`在這個題目用的是類似`math.Max()`的方法,回傳輸入參數中較大的一方
實際輸入的參數是:
1. $P_m + F(m, n - W_m)$ => `m`物體的價值,加上當前負重`n`扣掉`m`的重量$W_m$後的最大價值
2. $F(m-1, n)$ => 前一列的解,即是`res[i-1][j]`
實際開始跑回圈時,會遇到一個小問題:
當輸入$m = 3$時,並沒有前一列的解可以參考,導致`res[i-1][j]`會發生·`index out of range`
為此,我用了一個小技巧來解決:__將$W$的最前面塞入一個0__,並且在迴圈中判斷,當$m = 0$時,res[i][j]一律為0
```go=
sourceArr := []int{0, 3, 4, 7, 8, 9}
...
if i == 0 {
res[i][j] = 0
} esle if{
...
}
...
```
這就像是初始化的效果,會產出一列全都是0的陣列,供之後`compareFunc`參考
如此一來,當$m = 3$輸入時,`compareFunc`的另一個輸入值將會是前一列初始化的0
[看結果](https://play.golang.org/p/TdrWlWlqSpx)
###### tags: `Computer Science` `Algorithm` `Dynamic Programing`