Bootstrap

代码随想录算法训练营第三十七天|1049. 最后一块石头的重量 II,494. 目标和,474.一和零

系列文章目录

代码随想录算法训练营第一天|数组理论基础,704. 二分查找,27. 移除元素
代码随想录算法训练营第二天|977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II
代码随想录算法训练营第三天|链表理论基础,203.移除链表元素,707.设计链表,206.反转链表
代码随想录算法训练营第四天|24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II,总结
代码随想录算法训练营第五天|哈希表理论基础,242.有效的字母异位词,349. 两个数组的交集,202. 快乐数,1. 两数之和
代码随想录算法训练营第六天|454.四数相加II,383. 赎金信,15. 三数之和,18. 四数之和,总结
代码随想录算法训练营第七天|344.反转字符串,541. 反转字符串II,卡码网:54.替换数字,151.翻转字符串里的单词,卡码网:55.右旋转字符串
代码随想录算法训练营第八天|28. 实现 strStr(),459.重复的子字符串,字符串总结,双指针回顾
代码随想录算法训练营第九天|理论基础,232.用栈实现队列,225. 用队列实现栈
代码随想录算法训练营第十天|20. 有效的括号,1047. 删除字符串中的所有相邻重复项,150. 逆波兰表达式求值
代码随想录算法训练营第十一天|239. 滑动窗口最大值,347.前 K 个高频元素,总结
代码随想录算法训练营第十二天|理论基础,递归遍历,迭代遍历,统一迭代
代码随想录算法训练营第十三天|层序遍历10,226.翻转二叉树,101.对称二叉树
代码随想录算法训练营第十四天|104.二叉树的最大深度,559.n叉树的最大深度,111.二叉树的最小深度,222.完全二叉树的节点个数
代码随想录算法训练营第十五天|110.平衡二叉树,257. 二叉树的所有路径,404.左叶子之和
代码随想录算法训练营第十六天|513.找树左下角的值,112. 路径总和,113.路径总和ii,106.从中序与后序遍历序列构造二叉树,105.从前序与中序遍历序列构造二叉树
代码随想录算法训练营第十七天|654.最大二叉树,617.合并二叉树,700.二叉搜索树中的搜索,98.验证二叉搜索树
代码随想录算法训练营第十八天|530.二叉搜索树的最小绝对差,501.二叉搜索树中的众数,236. 二叉树的最近公共祖先
代码随想录算法训练营第十九天|235. 二叉搜索树的最近公共祖先,701.二叉搜索树中的插入操作,450.删除二叉搜索树中的节点
代码随想录算法训练营第二十天|669. 修剪二叉搜索树,108.将有序数组转换为二叉搜索树,538.把二叉搜索树转换为累加树,总结篇
代码随想录算法训练营第二十一天|回溯算法理论基础,77. 组合
代码随想录算法训练营第二十二天|216.组合总和III,17.电话号码的字母组合
代码随想录算法训练营第二十三天|39. 组合总和,40.组合总和II,131.分割回文串
代码随想录算法训练营第二十四天|93.复原IP地址,78.子集,90.子集II
代码随想录算法训练营第二十五天|491.递增子序列,46.全排列,47.全排列 II
代码随想录算法训练营第二十六天|332.重新安排行程,51. N皇后,37. 解数独,总结
代码随想录算法训练营第二十七天|贪心算法理论基础,455.分发饼干,376. 摆动序列,53. 最大子序和
代码随想录算法训练营第二十八天|122.买卖股票的最佳时机II,55. 跳跃游戏,45.跳跃游戏II
代码随想录算法训练营第二十九天|1005.K次取反后最大化的数组和,134. 加油站,135. 分发糖果
代码随想录算法训练营第三十天|860.柠檬水找零,406.根据身高重建队列,452. 用最少数量的箭引爆气球
代码随想录算法训练营第三十一天|435. 无重叠区间,763.划分字母区间,56. 合并区间
代码随想录算法训练营第三十二天|738.单调递增的数字,968.监控二叉树,总结
代码随想录算法训练营第三十三天|动态规划理论基础,509. 斐波那契数,70. 爬楼梯,746. 使用最小花费爬楼梯
代码随想录算法训练营第三十四天|62.不同路径,63. 不同路径 II
代码随想录算法训练营第三十五天|343. 整数拆分,96.不同的二叉搜索树
代码随想录算法训练营第三十六天|背包理论基础,416. 分割等和子集


1049. 最后一块石头的重量 II

题目链接: 1049. 最后一块石头的重量 II
题目内容: 有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:如果 x == y,那么两块石头都会被完全粉碎;如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。
视频讲解: 动态规划之背包问题,这个背包最多能装多少?LeetCode:1049.最后一块石头的重量 II

思路:尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了。

动态规划问题的五步曲:

  • 确定dp数组(dp table)以及下标的含义:
    01背包中,dp[j]的含义,容量为j的背包,最多可以装的价值为 dp[j]。相对于 01背包,本题中,石头的重量是 stones[i],石头的价值也是 stones[i] ,因此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数组如何初始化:dp数组初始化为0

  • 确定遍历顺序:物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!

  • 举例推导dp数组

class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        #初始化
        dp=[0]*15001
        total_sum=sum(stones)
        target=total_sum // 2
        for stone in stones: #遍历物品
            for j in range(target,stone-1,-1): #遍历背包
                dp[j]=max(dp[j],dp[j-stone]+stone)
        return total_sum-dp[target]-dp[target]

494. 目标和

题目链接: 494. 目标和
题目内容: 给你一个非负整数数组 nums 和一个整数 target 。向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
视频讲解: 动态规划之背包问题,装满背包有多少种方法?| LeetCode:494.目标和

思路: 转化为01背包问题:假设加法的总和为x,那么减法对应的总和就是sum - x。所以我们要求的是 x - (sum - x) = target,x = (target + sum) / 2。此时问题就转化为,装满容量为x的背包,有几种方法。

动态规划问题的五步曲:

  • 确定dp数组(dp table)以及下标的含义:填满j(包括j)这么大容积的包,有dp[j]种方法

  • 确定递推公式:dp[j] += dp[j - nums[i]]

  • dp数组如何初始化:从递推公式可以看出,在初始化的时候dp[0] 一定要初始化为1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递推结果将都是0。

  • 确定遍历顺序:nums放在外循环,target在内循环,且内循环倒序

  • 举例推导dp数组

class Solution:
    def findTargetSumWays(self, nums: List[int], target: int) -> int:
        total_sum=sum(nums)
        if abs(target)>total_sum:
            return 0
        if (target+total_sum)%2==1:
            return 0
        target_sum=(target+total_sum)//2
        dp=[0]*(target_sum+1)
        dp[0]=1
        for num in nums:
            for j in range(target_sum,num-1,-1):
                dp[j]+=dp[j-num]
        return dp[target_sum]

474.一和零

题目链接: 474.一和零
题目内容: 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
视频讲解: 动态规划之背包问题,装满这个背包最多用多少个物品?| LeetCode:474.一和零

思路: 转化为01背包问题:本题中strs 数组里的元素就是物品,每个物品都是一个;而m 和 n相当于是一个背包,两个维度的背包。

动态规划问题的五步曲:

  • 确定dp数组(dp table)以及下标的含义:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]

  • 确定递推公式:dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1)

  • dp数组如何初始化:初始化为0就可以。因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖

  • 确定遍历顺序:先遍历物品,再遍历背包,倒序遍历,背包有两个维度,这两个维度的遍历顺序先遍历哪一个都可以

  • 举例推导dp数组

class Solution:
    def findMaxForm(self, strs: List[str], m: int, n: int) -> int:
        dp=[[0]*(n+1) for _ in range(m+1)]
        for s in strs: #遍历物品
            zeroNum=s.count('0')
            oneNum=len(s)-zeroNum
            for i in range(m,zeroNum-1,-1): #遍历背包
                for j in range(n,oneNum-1,-1):
                    dp[i][j]=max(dp[i][j],dp[i-zeroNum][j-oneNum]+1)
        return dp[m][n]
;