1049. 最后一块石头的重量Ⅱ
题目描述
有一堆石头,用整数数组 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]stones = [2,7,4,1,8,1]stones=[2,7,4,1,8,1]
输出:111
示例1:
输入:stones=[31,26,33,21,40]stones = [31,26,33,21,40]stones=[31,26,33,21,40]
输出:555
思路
本题与分割子集很像,物品重量为stones[i],物品的价值也为stones[i]。对应01背包里面物品重量和物品价值。
dp[j]表示的是重量为j的背包最多可以背最大重量为dp[j]。
解法
class Solution {
public int lastStoneWeightII(int[] stones) {
int sum = 0;
for(int i : stones){
sum += i;
}
int target = sum/2;
int[] dp = new int[target+1];
for(int i = 0;i<stones.length;i++){
for(int j = target;j>=stones[i];j--){
dp[j] = Math.max(dp[j],dp[j-stones[i]]+stones[i]);
}
}
return sum - 2*dp[target];
}
}
总结
好像是有点找到01背包的感觉了!
494. 目标和
题目描述
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
示例1:
输入:nums=[1,1,1,1,1],target=3nums = [1,1,1,1,1], target = 3nums=[1,1,1,1,1],target=3
输出:555
示例2:
输入:nums=[1],target=1nums = [1], target = 1nums=[1],target=1
输出:111
思路
说实话,看起来仿佛更像是个回溯,有一种想要暴搜的冲动。
没什么背包的感觉,直接上答案了。
1、假设加法的总和为x,那么减法对应的总和就是sum-x,所以达到运算结果等于target的要求,也就是要计算x-(sum-x)=target,也就是,x = (target+sum)/2.
而这时,问题就转化为了装满容量为x的背包,有几种方法。
2、确定递推公式
推出dp[j]可以由nums[i]推出,只要有nums[i],那么凑成dp[j]就有dp[j-nums[i]]种方法。即,dp[j] += dp[j-nums[i]]
解法
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
for(int i = 0;i<nums.length;i++){
sum += nums[i];
}
if(Math.abs(target) > sum){
return 0;
}
if((target + sum)%2 != 0){
return 0;
}
int size = (target+sum)/2;
if(size < 0){
size = -size;
}
int[] dp = new int[size+1];
dp[0] = 1;
for(int i = 0;i<nums.length;i++){
for(int j = size;j>=nums[i];j--){
dp[j] += dp[j-nums[i]];
}
}
return dp[size];
}
}
总结
动态规划还有很多东西要学啊,没见过,再看看。
474. 一和零
题目描述
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
示例1:
输入:strs=["10","0001","111001","1","0"],m=5,n=3strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3strs=["10","0001","111001","1","0"],m=5,n=3
输出:444
示例2:
输入:strs=["10","0","1"],m=1,n=1strs = ["10", "0", "1"], m = 1, n = 1strs=["10","0","1"],m=1,n=1
输出:222
思路
个人认为已经是一个挺难的题了,直接上答案思路了。
首先,最重要的是要确定本题是一个01背包问题,这其实还挺不容易的。本题中,strs中的元素就是物品,每个物品均为一个,而m和n相当于是一个背包,一个二维的背包,而不是两个背包。
1、确定dp[i][j]以及其下标的定义:
dp[i][j]表示:最多有i个0和j个1的strs的最大子集的大小。
这我得想啥年才能想到这个复杂的定义啊。
2、确定递推公式
dp[i][j]可以由前一个strs里面的字符串推导出来,strs中的字符串由zeroNum个0和oneNum个1。
故而dp[i][j]就可以使dp[i-zeroNum][j-oneNum]+1
再遍历过程中,需要取得dp[i][j]的最大值,
所以递推公式:dp[i][j] = max(dp[i][j],dp[i-zeroNum][j-oneNum]+1)
解法
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
int[][] dp = new int[m+1][n+1];
int oneNum,zeroNum;
for(String str : strs){
oneNum = 0;
zeroNum = 0;
for(char ch : str.toCharArray()){
if(ch == '0'){
zeroNum++;
}
else{
oneNum++;
}
}
for(int i = m; i>=zeroNum;i--){
for(int j = n;j >= oneNum;j--){
dp[i][j] = Math.max(dp[i][j],dp[i-zeroNum][j-oneNum]+1);
}
}
}
return dp[m][n];
}
}
总结
让我大喊出那一句:想不到啊,我根本想不到啊。动态规划真的好难啊。
文章通过三个编程题展示了动态规划和回溯在解决石头重量、目标和、一和零问题中的应用,解释了解题思路和代码实现,强调了问题转化为背包问题的重要性。
1313

被折叠的 条评论
为什么被折叠?



