问题描述:
给定N中物品和一个背包。物品i的重量是Wi,其价值位Vi ,背包的容量为C。问应该如何选择装入背包的物品,使得转入背包的物品的总价值为最大??
在选择物品的时候,对每种物品i只有两种选择,即装入背包或不装入背包。不能讲物品i装入多次,也不能只装入物品的一部分。因此,该问题被称为0-1背包问题。
问题分析:令V(i,j)表示在前i(1<=i<=n)个物品中能够装入容量为就j(1<=j<=C)的背包中的物品的最大价值,则可以得到如下的动态规划函数:
(1) V(i,0)=V(0,j)=0
(2) V(i,j)=V(i-1,j) j<wi
V(i,j)=max{V(i-1,j) ,V(i-1,j-wi)+vi) } j>wi
(1)式表明:如果第i个物品的重量大于背包的容量,则装人前i个物品得到的最大价值和装入前i-1个物品得到的最大价是相同的,即物品i不能装入背包;第(2)个式子表明:如果第i个物品的重量小于背包的容量,则会有一下两种情况:(a)如果把第i个物品装入背包,则背包物品的价值等于第i-1个物品装入容量位j-wi 的背包中的价值加上第i个物品的价值vi; (b)如果第i个物品没有装入背包,则背包中物品价值就等于把前i-1个物品装入容量为j的背包中所取得的价值。显然,取二者中价值最大的作为把前i个物品装入容量为j的背包中的最优解。
package main
import (
"fmt"
)
const (
N = 4
W = 5
)
var weight []int = []int{3, 2, 1, 2}
var val []int = []int{4, 3, 2, 2}
var dp [N + 1][W + 1]int
var record [N][W + 1]int //保存中间结果的记录
func init() {
for i := 0; i < N; i++ {
for j := 0; j <= W; j++ {
record[i][j] = -1
}
}
}
//非递归方式 V(i,j)=max{V(i-1,j) ,V(i-1,j-wi)+vi) } j>wi
func solve2() int {
length := len(val)
for i := 0; i < length; i++ {
for j := weight[i]; j <= W; j++ {
dp[i+1][j] = max(dp[i][j], dp[i][j-weight[i]]+val[i])
}
}
return dp[N][W]
}
//递归方式
func solve(i int, total int) int {
result := 0
if i >= N {
return result
}
if -1 != record[i][total] {
return record[i][total]
}
if weight[i] > total {
record[i][total] = solve(i+1, total)
} else {
//递归求最大值
result = max(solve(i+1, total), solve(i+1, total-weight[i])+val[i])
}
record[i][total] = result
return record[i][total]
}
func max(a int, b int) int {
if a > b {
return a
}
return b
}
func main() {
result := solve(0, W)
fmt.Printf("best val:%v \n", result)
result1 := solve2()
fmt.Printf("best value1:%v\n", result1)
}
总结:递归实现动态规划求第N+1次最最优解与第N次最优解之间有关联,可以理解成裁剪过的探索与回溯算法