代码随想录day37|1049最后一块石头的重量494目标和474一和零|01笔记

78 阅读4分钟
  • 1049最后一块石头的重量

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 本题的逻辑可以转化为,将整个数组分为两个子集,让两个子集和的差最小,或者说使其中一个子集的和更接近总和的一半。可以利用类似416分割等和子集的方法,当背包容量为target/2时,就可以得到其中少的子集。
  • 讲解观后感

  • ·确定dp数组以及下标的含义
    dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j]
  • ·确定递推公式
    01背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
    本题则是:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);
  • ·dp数组如何初始化
  • 计算出石头总重量 然后除2,得到dp数组的大小。
  • 接下来就是如何初始化dp[j]呢,因为重量都不会是负数,所以dp[j]都初始化为0就可以了,这样在递归公式dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);中dp[j]才不会初始值所覆盖。
  • ·
  • 确定遍历顺序
    动态规划:关于01背包问题,你该了解这些!(滚动数组) (opens new window)中就已经说明:如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!
  • 解题代码

  •     func lastStoneWeightII(stones []int) int {
        	// 15001 = 30 * 1000 /2 +1
        	dp := make([]int, 15001)
        	// 求target
        	sum := 0
        	for _, v := range stones {
        		sum += v
        	}
        	target := sum / 2
        	// 遍历顺序
        	for i := 0; i < len(stones); i++ {
        		for j := target; j >= stones[i]; j-- {
        			// 推导公式
        			dp[j] = max(dp[j], dp[j-stones[i]]+stones[i])
        		}
        	}
        	return sum - 2 * dp[target]
        }
        
        func max(a, b int) int {
        	if a > b {
        		return a
        	}
        	return b
        }
    
  • 494目标和

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 可以让所有的数字一开始即为负数,然后当改变其中一个数字的符号时,相当于在背包中放入价值二倍的物品,背包初始价值是负数的总和。我们要统计所有价值为S的方法。
  • 讲解观后感

  • 卡尔使用了不同的推理方式初始化背包容量,这种方法能够更好的统计方法数量。我采用不同的描述方式进行了整理。
    • 假设正数的总和为x,那么负数对应的总和就是-(sum - x)。(sum为所有数字正相加)
      所以 x +(- (sum - x)) = target
    • 所以x = (target + sum) / 2
  • 我们定义dp数组以及下标的含义。dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法
  • 确定递推公式
  • 例如:dp[j],j 为5,
    • 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包。
    • 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包。
    • 已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
    • 已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
    • 已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包
  • 那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来。
  • 所以递推公式
  •     dp[j] += dp[j - nums[i]]
    
  • 初始化dp[0]=1,内循环倒序
  • 解题代码

  •     func findTargetSumWays(nums []int, target int) int {
        	sum := 0
        	for _, v := range nums {
        		sum += v
        	}
        	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)))
        }
    
  • 474一和零

  • 代码随想录 (programmercarl.com)
  • 讲解观后感

  • 本题还是01背包的问题,只不过背包容量是由m和n两个维度构成的。而每个物品就是字符串数组中的字符串。
  • 使用dp[i][j]表示容量m=i、n=j时最多有i个0和j个1的strs的最大子集的大小为dp[i][j]
  • 但此时的递推公式其实是一维的,因为"物品"维度是字符串。
  • 递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
  • 初始dp[0][0]=0即可
  • 那么本题也是,物品就是strs里的字符串,背包容量就是题目描述中的m和n。外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!
  • 解题代码

  •     func findMaxForm(strs []string, m int, n int) int {
        	// 定义数组
        	dp := make([][]int, m+1)
        	for i,_ := range dp {
        		dp[i] = make([]int, n+1 )
        	}
        	// 遍历
        	for i:=0;i<len(strs);i++ {
        		zeroNum,oneNum := 0 , 0
        		//计算0,1 个数
        		//或者直接strings.Count(strs[i],"0")
        		for _,v := range strs[i] {
        			if v == '0' {
        				zeroNum++
        			}
        		}
        		oneNum = len(strs[i])-zeroNum
        		// 从后往前 遍历背包容量
        		for j:= m ; j >= zeroNum;j-- {
        			for k:=n ; k >= oneNum;k-- {
        				// 推导公式
        				dp[j][k] = max(dp[j][k],dp[j-zeroNum][k-oneNum]+1)
        			}
        		}
        		//fmt.Println(dp)
        	}
        	return dp[m][n]
        }
        
        func max(a,b int) int {
        	if a > b {
        		return a
        	}
        	return b
        }