代码随想录训练营第43天|LeetCode 1049. 最后一块石头的重量 II、494. 目标和、474.一和零

参考

代码随想录

题目一:LeetCode 1049. 最后一块石头的重量 II

这个题和之前的“416 分割等和子集”非常的类似,如果给出的stones[]数组能被分成相等的两个数组,那么剩余石头的最小重量就是0。求解思路和分割等和子集一样,只是最后的返回值不同。套用01背包,物品的重量和价值都是stones[]。

  1. 确定dp数组及其下标的含义
    dp[j] :背包最大承载重量j得到的最大价值为dp[j],对应本题,最大总和j对应的实际能达到的最大总和为dp[j]。
  2. 确定递推关系
    一维dp数组01背包的递推公式如下:
dp[j] = max(dp[j],dp[j-stones[i]]+stones[i])
  1. dp数组初始化
    题目中给出1 <= stones[i] <= 100,因此dp数组全部初始化为0。
  2. 确定遍历顺序
    遍历顺序在一维dp数组01背包中已经分析过了,如下:
for(int i = 0; i < stones.size(); i++){
    for(int j = target; j >= stones[i]; j--){
        dp[j] = max(dp[j],dp[j - stones[i]] + stones[i]);
    }
}
  1. 举例推导dp数组
    以stones = [2,4,1,1]为例,推导得到的dp数组如下:
    在这里插入图片描述

整体代码实现如下:

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum = 0;
        for(int i = 0; i < stones.size(); i++)  
            sum += stones[i];
        int target = sum / 2;
        vector<int> dp(target+1,0);
        for(int i = 0; i < stones.size(); i++){
            for(int j = target; j >= stones[i]; j--){
                dp[j] = max(dp[j],dp[j - stones[i]] + stones[i]);
            }
        }
        return sum - dp[target] * 2;
    }
};

题目二:LeetCode 494. 目标和

假设添加“+”或者“-”之后,加法总和为x,则减法总和为sum-x(注意这里指的是添加减号的那些数的总和),其中sum是添加符号之前的数组总和,则有

target = x + (-(sum-x)) = 2x-sum

因此可以得到x的表达式:

x = (sum + target) / 2

因此将问题转化为数组中有多少种和为x的组合,套用01背包,就是装满容量为x的背包,有多少种方法,此时的背包容量bagSize就是这里的x。
因为给定的元素都为整数,所以如果sum+target为奇数的话是不可能得到和为x的组合的,因为奇数除以2之后结果会被截断。

  1. 明确dp数组及其下标的含义
    dp[j]:装满容量为j的背包有dp[j]种方法
  2. 确定递推公式
    对于数组中的某一个元素nums[i],得到dp[j]可以有dp[j - nums[i]]种方法,而nums[i]可以是数组中满足条件的任一个数,因此可以得到递推公式:
dp[j] += dp[j - nums[i]]
  1. 初始化dp数组
    dp[0] = 1,其他初始化为0
  2. 确定遍历顺序
    nums放在外循环,target在内循环,且内循环倒序。
  3. 举例推导递推公式
    nums = [1,1,1,1,1],target = 3
    在这里插入图片描述
    完整的代码实现如下:
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for(int i = 0; i < nums.size(); i++)
            sum += nums[i];
        if((sum + target) % 2 == 1) return 0;
        if(sum < abs(target))    return 0;
        int bagSize = (sum + target) / 2;
        vector<int> dp(bagSize+1,0);
        dp[0] = 1;
        for(int i = 0; i < nums.size(); i++){
            for(int j = bagSize; j >= nums[i]; j--){
                dp[j] += dp[j - nums[i]];
            }
        }
        return dp[bagSize];
    }
};

题目三:LeetCode 474.一和零

本题中strs 数组里的元素就是物品,每个物品都是一个,而m 和 n相当于是一个背包,两个维度的背包。

  1. 确定dp数组及其下标的含义
    dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]。
  2. 确定递推公式
dp[i][j] = max(dp[i][j],dp[i-zeroNum][j-oneNum]+1)
  1. dp数组初始化
    因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。
  2. 确定遍历顺序
    01背包一定是外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!
for (string str : strs) { // 遍历物品
    int oneNum = 0, zeroNum = 0;
    for (char c : str) {
        if (c == '0') zeroNum++;
        else oneNum++;
    }
    for (int i = m; i >= zeroNum; i--) { // 遍历背包容量且从后向前遍历!
        for (int j = n; j >= oneNum; j--) {
            dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
        }
    }
}
  1. 举例推导dp数组
    strs = [“10”,“0001”,“111001”,“1”,“0”],m = 3,n = 3
    在这里插入图片描述
    完整的代码实现如下:
class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>> dp(m + 1, vector<int> (n + 1, 0)); // 默认初始化0
        for (string str : strs) { // 遍历物品
            int oneNum = 0, zeroNum = 0;
            for (char c : str) {
                if (c == '0') zeroNum++;
                else oneNum++;
            }
            for (int i = m; i >= zeroNum; i--) { // 遍历背包容量且从后向前遍历!
                for (int j = n; j >= oneNum; j--) {
                    dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
                }
            }
        }
        return dp[m][n];
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
第二十二天的算法训练主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忆昔z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值