代码随想录算法训练营day43 | 1049. 最后一块石头的重量 II ,494. 目标和,474.一和零
- 1049. 最后一块石头的重量 II
- 解法一:动态规划
- 494. 目标和
- 解法一:动态规划
- 474.一和零
- 解法一:动态规划
- 01背包问题总结
1049. 最后一块石头的重量 II
教程视频:https://www.bilibili.com/video/BV14M411C7oV
思路:尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就将本题化解成01背包问题了。
此时背包容量是sum/2,物品重量和价值都等于数组中的每个数字。
解法一:动态规划
1、dp[j]含义:将数值放入容量为j的背包中能得到的最大重量。
2、递推公式:dp[j]=Math.max(dp[j], dp[j-stones[i]]+stones[i]);
3、dp初始化:dp[0]=0;重量都大于等于1,为正整数,递推过程中小的值会被覆盖,因此其他索引的值初始化成0就可以
4、遍历顺序:外层for控制放入被报道石头,内层for反向遍历背包容量
5、打印验证
class Solution {
public int lastStoneWeightII(int[] stones) {
int sum = 0;
for(int i=0;i<stones.length;i++){
sum +=stones[i];
}
//创建dp数组
int bagSize = sum/2;
int[] dp = new int[bagSize+1];
//java默认初始化为0,不需要显示初始化
for(int i=0;i<stones.length;i++){
for(int j=bagSize;j>=stones[i];j--){
//两种情况,要么放,要么不放
dp[j] = Math.max(dp[j],dp[j-stones[i]]+stones[i]);
}
}
return sum-2*dp[bagSize];
}
}
494. 目标和
教程视频:https://www.bilibili.com/video/BV1o8411j73x
思路:将所有数值划分为加的数(positive)和减的数(negative),sum=positive+negative,根据题意需要满足positive-negative=target。可以转换成:positive-(sum-positive)=target,由于sum和target已知,positive=(sum+target)/2,这就是背包容量。如果positive不为整数,说明nums不能凑成target(可以剪枝)。
解法一:动态规划
1、dp[j]定义:和为j的表达式数目。
2、递推公式:dp[j]+=dp[j-nums[i]];(只要搞到nums[i],就有dp[j - nums[i]] 种方法可以凑成dp[j]。
3、dp初始化:因为递推公式是累加,dp[0]不能为0,可以想成和为0就只有所有值都不放入背包这一种方式,dp[0]=1;其他都是加法累计过来的,初始化为0。
4、遍历顺序:外层for遍历nums,内层for倒序遍历背包容量。
5、打印验证。
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum=0;
for(int i=0;i<nums.length;i++){
sum+=nums[i];
}
//如果target绝对值大于sum,将无法满足,返回0
if( target > sum)return 0;
if ( target < 0 && sum < -target) return 0;
//如果sum+target/2为小数,将不能得到target,返回0
if((sum+target)%2 == 1)return 0;
int bagSize = (sum+target)/2;//经过上面的判断,bagSize一定大于等于0
int[] dp = new int[bagSize+1];
dp[0]=1;
for(int i=0;i<nums.length;i++){
for(int j=bagSize;j>=nums[i];j--){
dp[j]+=dp[j-nums[i]];
}
}
return dp[bagSize];
}
}
474.一和零
教程视频:https://www.bilibili.com/video/BV1rW4y1x7ZQ
思路:本题类似1049. 最后一块石头的重量 II,但是本题的背包容量有两个维度(可以理解为从重量和体积两个维度限制了放入为物品),因此背包需要使用两层for循环来反向遍历。
解法一:动态规划
1、dp[j][k]含义:背包容量为 j 个0和 k个1时,最大子集长度。
2、递推公式:dp[j][k]=Math.max(dp[j][k], dp[j-a][k-b]+1);其中 a为strs中0的个数,b为strs中1的个数。
3、dp初始化:dp[0][0]=0;,对于其他非0下标,都会被max迭代,所以取最小值0即可。
4、遍历顺序:外层for循环遍历strs,内层两个for循环倒序遍历背包容量。
5、打印验证
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
//创建dp数组,dp[j][k]表示j个0和k个1时的最大子集
int[][] dp = new int[m+1][n+1];
//初始化dp数组,默认为0,无需显示初始化
for(int i=0;i<strs.length;i++){
//统计strs[i]中0和1的个数
int a=0,b=0;
for(int index=0;index<strs[i].length();index++){
if(strs[i].charAt(index)=='0'){
a++;
}else{
b++;
}
}
// System.out.println(strs[i]+"中,0的个数="+a+", 1的个数="+b);
for(int j=m;j>=a;j--){
for(int k=n;k>=b;k--){
dp[j][k]=Math.max(dp[j][k], dp[j-a][k-b]+1);
}
}
}
return dp[m][n];
}
}
01背包问题总结
此时我们讲解了0-1背包的多种应用,
- 纯 0 - 1 背包 是求 给定背包容量 装满背包 的最大价值是多少。dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);输出dp[dp.length-1];
- 416.分割等和子集是求 给定背包容量,能不能装满这个背包。dp[j]=Math.max(dp[j], dp[j-nums[i]]+nums[i]);输出dp[sum/2] == sum/2;
- 1049.最后一块石头的重量 II是求 给定背包容量,尽可能装,最多能装多少。dp[j]=Math.max(dp[j], dp[j-stones[i]]+stones[i]);输出sum-2*dp[bagSize];
- 494.目标和 是求 给定背包容量,装满背包有多少种方法。dp[j]+=dp[j-nums[i]];输出dp[bagSize];
- 474.一和零 是求 给定背包容量,装满背包最多有多少个物品。dp[j][k]=Math.max(dp[j][k], dp[j-a][k-b]+1);输出dp[m][n];