1.目标和
这里设置背包的最大长度为2100即可,因为题目中有说数组之和小于1000.但考虑到我们需要实行j+nums[i]所以保守起见我们设置的数应该稍大于2000即可,这里我们设置为2100。
1.1 我的解法(粗糙了)
func findTargetSumWays(nums []int, target int) int {
//dp[i][j],这里的i代表nums中的前i个数,j代表target
n := len(nums)
dp := make([][]int, n)
maxNum := 2100
for i := 0; i < n; i++ {
dp[i] = make([]int, maxNum+1)
}
if nums[0] == 0 {
dp[0][nums[0]] = 2
} else {
dp[0][nums[0]] = 1
}
for i := 1; i < n; i++ {
for j := 0; j < maxNum-1000; j++ {
target1 := abs(j - nums[i])
target2 := abs(j + nums[i])
if target1 != target2 || j == 0 || nums[i] == 0 {
dp[i][j] = dp[i-1][target1] + dp[i-1][target2]
} else {
dp[i][j] = dp[i-1][target1]
}
}
}
return dp[n-1][abs(target)]
}
func abs(a int) int {
if a > 0 {
return a
}
return -a
}
1.2 它解
假设加法的总和为x,那么减法对应的总和就是sum - x。
所以我们要求的是 x - (sum - x) = target
x = (target + sum) / 2
此时问题就转化为,装满容量为x的背包,有几种方法。
这里的x,就是bagSize,也就是我们后面要求的背包容量。
大家看到(target + sum) / 2 应该担心计算的过程中向下取整有没有影响。
这么担心就对了,例如sum 是5,S是2的话其实就是无解的。
func findTargetSumWays(nums []int, target int) int {
sum := 0
for _, v := range nums {
sum += v
}
//target的绝对值已经大于sum
if abs(target) > sum {
return 0
}
//要么全为奇数,要么全为偶数
if (sum+target)%2 == 1 {
return 0
}
// 计算背包大小
bag := (sum + target) / 2
// 定义dp数组
dp := make([]int, bag+1)
// 初始化
dp[0] = 1
// 遍历顺序
for i := 0; i < len(nums); i++ {
for j := bag; j >= nums[i]; j-- {
//推导公式
dp[j] += dp[j-nums[i]]
//fmt.Println(dp)
}
}
return dp[bag]
}
func abs(x int) int {
return int(math.Abs(float64(x)))
}
2.一和零
思路:这里将i表示0的个数,j表示1的个数
当i大于等于字符串中0的个数且j大于等于字符串中1的个数时,可以得到递推公式为
dp[i][j] = max(dp[i][j], dp[i-itemList[k].num0][j-itemList[k].num1]+1)
func findMaxForm(strs []string, m int, n int) int {
dp := make([][]int, m+1)
for i := 0; i <= m; i++ {
dp[i] = make([]int, n+1)
}
itemList := make([]*Item, 0)
for i := 0; i < len(strs); i++ {
num0, num1 := getCountOf01(strs[i])
itemList = append(itemList, &Item{num0: num0, num1: num1})
}
//i表示0的个数,j表示1的个数
for k := 0; k < len(strs); k++ {
for i := m; i >= itemList[k].num0; i-- {
for j := n; j >= itemList[k].num1; j-- {
dp[i][j] = max(dp[i][j], dp[i-itemList[k].num0][j-itemList[k].num1]+1)
}
}
//for i := 0; i <= m; i++ {
// fmt.Println(dp[i])
//}
//fmt.Println("==========")
}
return dp[m][n]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
type Item struct {
num0, num1 int
}
func getCountOf01(str string) (num0, num1 int) {
for i := 0; i < len(str); i++ {
if str[i:i+1] == "0" {
num0++
} else if str[i:i+1] == "1" {
num1++
}
}
return
}
3.完全背包理论基础
https://kamacoder.com/problempage.php?pid=1052
和零一背包的区别在于,这里面的材料可以重复拾取。
// test_CompletePack1 先遍历物品, 在遍历背包
func test_CompletePack1(weight, value []int, bagWeight int) int {
// 定义dp数组 和初始化
dp := make([]int, bagWeight+1)
// 遍历顺序
for i := 0; i < len(weight); i++ {
// 正序会多次添加 value[i]
for j := weight[i]; j <= bagWeight; j++ {
// 推导公式
dp[j] = max(dp[j], dp[j-weight[i]]+value[i])
// debug
//fmt.Println(dp)
}
}
return dp[bagWeight]
}
// test_CompletePack2 先遍历背包, 在遍历物品
func test_CompletePack2(weight, value []int, bagWeight int) int {
// 定义dp数组 和初始化
dp := make([]int, bagWeight+1)
// 遍历顺序
// j从0 开始
for j := 0; j <= bagWeight; j++ {
for i := 0; i < len(weight); i++ {
if j >= weight[i] {
// 推导公式
dp[j] = max(dp[j], dp[j-weight[i]]+value[i])
}
// debug
//fmt.Println(dp)
}
}
return dp[bagWeight]
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
4.零钱兑换II
func change(amount int, coins []int) int {
// 定义dp数组
dp := make([]int, amount+1)
// 初始化,0大小的背包, 当然是不装任何东西了, 就是1种方法
dp[0] = 1
// 遍历顺序
// 遍历物品
for i := 0; i < len(coins); i++ {
// 遍历背包
for j := coins[i]; j <= amount; j++ {
// 推导公式
dp[j] += dp[j-coins[i]]
}
}
return dp[amount]
}