Leetcode刷题——动态规划练习(0-1背包系列)
动态规划(0-1背包系列)
102目标和
题目描述
给定一个正整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
示例 1:
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
示例 2:
输入:nums = [1], target = 1
输出:1
提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000
题目求解
classSolution:deffindTargetSumWays(self,nums:List[int],target:int)->int:# 使用动态规划# 0-1背包 :刚好装满 背包的方案数量# 前面+ 的数字之和:A# 前面- 的数字之和:B# 满足 A - B = target# 并且 A + B = sum(nums)# 则A = (target+sum) / 2# 背包容量为A,找 能恰好装满A容量的方案数item=target+sum(nums)ifitem%2!=0:return0# 找不到方案A=item//2dp=[0]*(A+1)dp[0]=1fornuminnums:foriinrange(A,num-1,-1):dp[i]+=dp[i-num]returndp[A]最后一块石头的重量
题目描述
有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。
示例:
输入:[2,7,4,1,8,1]
输出:1
解释:
先选出 7 和 8,得到 1,所以数组转换为 [2,4,1,1,1],
再选出 2 和 4,得到 2,所以数组转换为 [2,1,1,1],
接着是 2 和 1,得到 1,所以数组转换为 [1,1,1],
最后选出 1 和 1,得到 0,最终数组转换为 [1],这就是最后剩下那块石头的重量。
提示:
1 <= stones.length <= 30
1 <= stones[i] <= 1000
题目求解
大顶堆求解:使用heapq;默认是小顶堆,全变为负数就是大顶堆了,取值得时候取反。
importheapqclassSolution:deflastStoneWeight(self,stones:List[int])->int:heap=[-sforsinstones]heapq.heapify(heap)whilelen(heap)>1:y=-heapq.heappop(heap)x=-heapq.heappop(heap)ify>x:heapq.heappush(heap,-(y-x))return-heap[0]ifheapelse0最后一块石头的重量 Ⅱ
题目描述
有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。
每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。
示例 1:
输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。
示例 2:
输入:stones = [31,26,33,21,40]
输出:5
提示:
1 <= stones.length <= 30
1 <= stones[i] <= 100
题目求解
首先,类似于上一题,使用贪心模拟也可以做到,但不是最优解,使用DP(0-1背包)
题目本质:把石头分成两堆,让两堆的总和尽可能的接近,设:
- 总和 = sum
- 第一堆和 = S
- 第二堆和 = sum - S
最后剩余的重量就是:sum - 2 * S (让S尽可能接近sum / 2,但不超过sum / 2)
套进背包模型:
- 背包容量 = sum / 2
- 每个 石头重量 = 价值 = stores[i]
- 求:不超过容量的最大价值
最后答案: sum - 2 * dp[sum / 2]
代码:
classSolution:deflastStoneWeightII(self,stones:List[int])->int:stones_sum=sum(stones)target=stones_sum//2dp=[0]*(target+1)forstoneinstones:foriinrange(target,stone-1,-1):dp[i]=max(dp[i],dp[i-stone]+stone)returnstones_sum-2*dp[target]474. 一和零
题目描述
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
示例 1:
输入:strs = [“10”, “0001”, “111001”, “1”, “0”], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {“10”,“0001”,“1”,“0”} ,因此答案是 4 。
其他满足题意但较小的子集包括 {“0001”,“1”} 和 {“10”,“1”,“0”} 。{“111001”} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。
示例 2:
输入:strs = [“10”, “0”, “1”], m = 1, n = 1
输出:2
解释:最大的子集是 {“0”, “1”} ,所以答案是 2 。
提示:
1 <= strs.length <= 600
1 <= strs[i].length <= 100
strs[i] 仅由 ‘0’ 和 ‘1’ 组成
1 <= m, n <= 100
题目求解
classSolution:deffindMaxForm(self,strs:List[str],m:int,n:int)->int:dp=[[0]*(n+1)for_inrange(m+1)]forsinstrs:sum_0=s.count('0')sum_1=s.count('1')forjinrange(m,sum_0-1,-1):forkinrange(n,sum_1-1,-1):dp[j][k]=max(dp[j][k],dp[j-sum_0][k-sum_1]+1)returndp[m][n]