【代码随想录——动态规划——第三周】

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]
}
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值