0-1背包问题图解-Go语言
问题:给定n种物品和一个背包。物品i的重量是Wi,其价值为Vi,背包的容重量为C。应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
- 在选择装入背包的物品时,对每种物品i只有2种选择,即装入/不装入。
dp[i][j]
表示前i 件物品体积不超过j 的情况下能达到的最大价值。- 边界条件:
dp[1][j] = V[1],if j >= W[1]
和dp[i][1] = 0
- 状态转移方程
dp[i][j] = max(dp[i-1][j],dp[i-1][j-W[i]]+V[i]),j-W[i]>=0
对于这个问题我们要对这个二维dp数组有清晰地认识,对其遍历循序要有个清晰认识。
对于一个dp[i][j]
的值是由该图中的二维dp数组的上方与左上方的值来推算出来的。
参考代码:
package main
import (
"fmt"
)
func main() {
const maxN, maxW = 1000, 1000000
weight := []int{2, 2, 6, 5, 4}
values := []int{6, 3, 5, 4, 6}
n := len(weight)
mw := 10
// dp数组
dp := [maxN][maxW]int{}
var max = func(a, b int) int {
if a > b {
return a
}
return b
}
// 初始化边界条件
for j := 1; j <= mw; j++ {
if j >= weight[0] {
dp[1][j] = values[0]
}
}
// 状态遍历
for i := 2; i <= n; i++ {
for j := 2; j <= mw; j++ {
if j >= weight[i-1] {
// 状态转移方程
dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i-1]]+values[i-1])
} else {
dp[i][j] = dp[i-1][j]
}
}
}
// 最终结果
fmt.Println(dp[n][mw])
// 输出dp数组
for i := 1; i <= n; i++ {
for j := 1; j <= mw; j++ {
fmt.Print(dp[i][j], " ")
}
fmt.Println()
}
}
对于这这道题,如果所有可装入背包的物品不多的话,我还可以暴力枚举。因为每一个物品我们可以装入背包或不装入,这就对应着两种状态,这样我们可以使用二进制枚举。
参考代码:
package main
import "fmt"
func main() {
weight := []int{2, 2, 6, 5, 4}
values := []int{6, 3, 5, 4, 6}
n := len(weight)
m := 10
var max = func(a, b int) int {
if a > b {
return a
}
return b
}
vs, ws, res := 0, 0, 0
for i := 0; i < 1<<n; i++ { // 枚举所有可能的状态
vs, ws = 0, 0
for j := 0; j < n; j++ {
if i&(1<<j) > 0 { // 检查状态
vs += values[j]
ws += weight[j]
}
}
if ws <= m { // 筛选符合条件的状态
res = max(res, vs)
}
}
fmt.Println(res)
}