Given integer array wt[0...n-1] and val[0...n-1], denoting weight and value of n items respectively, given an integer W denoting total weight capacity of a knapsack, calculate a subset of n items so that the total value of the subset is maximum.
- Brute-Force algorithm Considering the ith item, can it be put into the knapsack? if the weight of ith item is bigger than the left capacity of knapsack, of course it can not be put into the bag, otherwise we can get the maximum of the two cases by putting it into the bag and not putting it into the bag. Recursively iterating all items, then we can get the answer, here is the code,
package main
import "fmt"
func max(i, j int) int {
if i > j {
return i
}
return j
}
func knapsack(wt []int, val []int, W int, n int) int {
if W == 0 || len(wt) != len(val) || n == 0 {
return 0
}
if wt[n-1] > W {
return knapsack(wt, val, W, n-1)
} else {
return max(knapsack(wt, val, W-wt[n-1], n-1)+val[n-1],
knapsack(wt, val, W, n-1))
}
}
func main() {
wt := []int{10, 20, 30}
val := []int{60, 100, 120}
W := 50
fmt.Println(knapsack(wt, val, W, len(val)))
}
There are n items, and two cases of putting it into the bag or not at each item position, so the time complexity is O(2^n).
Think carefully, there are optimal sub-problems that is computed repeatedly, if we store the sub-problem result in an array, then we can get rid of the repeated computation, which is called the dynamic programming algorithm.
- Dynamic programming According to the property of the problem, we can write the transition funciton, using an 2D array dp[i][w] to store the sub-problem results, in which i denotes the number of items to be considered, and w denotes the total capacity of the knapsack,
|--- dp[i-1][w] if wt[i-1] > w
|
dp[i][w] = |
|--- max(dp[i-1][w], val[i-1]+dp[i-1][w-wt[i-1]])
Here is the implementation of above algorithm,
package main
import "fmt"
func max(i, j int) int {
if i > j {
return i
}
return j
}
func knapsack(wt []int, val []int, W int) int {
if W == 0 || len(val) == 0 || len(wt) != len(val) {
return 0
}
n := len(wt)
dp := make([][]int, n+1)
for i := 0; i <= n; i++ {
dp[i] = make([]int, W+1)
}
for i := 0; i <= n; i++ {
for j := 0; j <= W; j++ {
if i == 0 || j == 0 {
dp[i][j] = 0
} else if wt[i-1] > j {
dp[i][j] = dp[i-1][j]
} else {
dp[i][j] = max(dp[i-1][j], dp[i-1][j-wt[i-1]]+val[i-1])
}
}
}
return dp[n][W]
}
func main() {
wt := []int{10, 20, 30}
val := []int{60, 100, 120}
W := 50
fmt.Println(knapsack(wt, val, W))
}
obviously the time complexity is O(n*W), where n is the number of items.