0.前言
01背包问题具体描述如下:
在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1Wn,与之相对应的价值为P1Pn,求出能获得的最大价值。
01背包是最简单的背包问题,在题中每种物品只有一个,所以对于每个物品只需要考虑选与不选两种情况即可。如果每个物品有无数个则为完全背包,数量不同的物品则为多重背包。本文中,主要针对01背包问题,给出简单思路及GO语言解答实现。
1.实现
01背包的实现根据dp数组的定义不同,主要有两种方法,分别如下。
1.1 使用二维dp数组
此种方法便于理解,但是空间复杂度更大。
dp[i][k]
的含义为当物品为0~i、容量为k时,此时能获得的最大的价值。
根据每次是否选取第i个物品,推出递推式:
dp[i][k] = max(dp[i-1][k], dp[i-1][k-weight[i]]+value[i])
在上式中,如果不选取,则dp[i][k]
的值会与dp[i-1][k]
相同,如果选取,则值为dp[i-1][k-weight[i]]+value[i]
。
package main
import "fmt"
func main() {
n:=0
cap:=0
fmt.Println("enter the number of objects:")
fmt.Scan(&n)
value :=make([]int,n)
weight:=make([]int,n)
fmt.Println("enter the value:")
for i := 0; i < n; i++ {
fmt.Scan(&value[i])
}
fmt.Println("enter the weight:")
for i := 0; i < n; i++ {
fmt.Scan(&weight[i])
}
fmt.Println("enter the capacity of bag:")
fmt.Scan(&cap)
fmt.Println("the answer is ",bag(weight,value,cap))
}
func bag(weight, value []int, cap int) int {
dp := make([][]int, len(weight))
for i := 0; i < len(weight); i++ {
dp[i] = make([]int, cap+1)
}
//初始化dp数组
for i := weight[0]; i <= cap; i++ {
dp[0][i] = value[0]
}
//遍历填充dp数组
for i := 1; i < len(dp); i++ {
for k := 1; k <= cap; k++ {
if k-weight[i] < 0 {
dp[i][k] = dp[i-1][k]
} else {
dp[i][k] = max(dp[i-1][k], dp[i-1][k-weight[i]]+value[i])
}
}
}
fmt.Println("the dp array is",dp)
return dp[len(weight)-1][cap]
}
func max(a int, b int) int {
if a > b {
return a
} else {
return b
}
}
1.2 使用一维dp数组
在使用二维数组时,i的存在不是必要的。因为物品已经给定,并不需要记录0~i的各个物品选取情况。
dp[j]
的含义为容量为j时,此时能获得的最大价值。
递推式与上述方法的核心是一样的,也是根据是否选取第i个物品来更新dp数组:
dp[j]=max(dp[j],dp[j-weight[i]]+value[i])
在编写代码时,思路也是一样,遍历每个物品,尝试将物品放入,如果放入后能获得更大价值,则更新dp数组的值。
注意:
- 观察递推式,
dp[j]
是根据dp[j-weight[i]]
(在dp[j]
的左边)推导而来。因此如果dp[j]从左往右更新,会产生错误,因为这个公式建立的基础就是假设dp[j-weight[i]]
的值为没有放入i物品的值。如果从左往右更新,可能会导致放入多次物品i。所以dp[j]的更新(遍历容量)要从右往左。
代码如下:
package main
import "fmt"
func main() {
n:=0
cap:=0
fmt.Println("enter the number of objects:")
fmt.Scan(&n)
value :=make([]int,n)
weight:=make([]int,n)
fmt.Println("enter the value:")
for i := 0; i < n; i++ {
fmt.Scan(&value[i])
}
fmt.Println("enter the weight:")
for i := 0; i < n; i++ {
fmt.Scan(&weight[i])
}
fmt.Println("enter the capacity of bag:")
fmt.Scan(&cap)
fmt.Println("the answer is ",bag(weight,value,cap))
}
func bag(weight, value []int, cap int) int {
dp:=make([]int,cap+1)
for i:=0;i<len(weight);i++{
for j:=cap;j>=weight[i];j--{
dp[j]=max(dp[j],dp[j-weight[i]]+value[i])
}
}
fmt.Println("the dp array is",dp)
return dp[cap]
}
func max(a int, b int) int {
if a > b {
return a
} else {
return b
}
}