代码随想录算法训练营day43|第九章 动态规划part05:1049. 最后一块石头的重量 II、494. 目标和、474.一和零

本文介绍了如何运用动态规划解决LeetCode中的背包问题,如1049最后一块石头的重量II,通过转化问题求解两个分数组的和的最小差值。同时提到了与416分割等和子集类似的方法,并对比了494目标和和474一和零的问题。
摘要由CSDN通过智能技术生成

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

本题就和 昨天的 416. 分割等和子集 很像了,可以尝试先自己思考做一做。 

视频讲解:动态规划之背包问题,这个背包最多能装多少?LeetCode:1049.最后一块石头的重量II_哔哩哔哩_bilibili

代码随想录

这道题需要间接考虑,可以转化为能不能将整个数组尽量分成两个和一样的分数组,这样求最后的石头的质量就是求两个分数组的和之间的最小差值,就变成了和。

416. 分割等和子集 (opens new window)差不多一个思路。还要注意一点是,无论这两道题哪一题都不能通过sum是偶数来直接得出结果,只是有这种恰好分成两个总和相等的分数组的可能性而已(其实只是前提条件,并不是等价),也并不一定如此。

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

494. 目标和 

大家重点理解 递推公式:dp[j] += dp[j - nums[i]],这个公式后面的提问 我们还会用到。  

视频讲解:动态规划之背包问题,装满背包有多少种方法?| LeetCode:494.目标和_哔哩哔哩_bilibili

代码随想录

感觉这道题真的好难懂啊!!!首先把它能套到动态规划就不太容易想到,文章中说其实对于每个数值选择运算符号,因为只有+、-号所以大可以将数组直接划分成两个分数组,plus数组和subtract数组,前者总和加上后者总和=sum,而前者总和减去后者总和=target,所以就可以推导出plus数组的总和=(sum+target)/2,那么这个数就相当于背包的容量。

首先还是dp数组的含义,dp[j] 代表总和为j 时,plus分数组的设置能有几种可行方法(一旦确定一个分数组,那么另一个也就定下来了,是剩下的元素),那么dp[j]+=dp[ j-nums[i] ] (后续涉及到方法数的大多都这个递归方式),也就是+确定加入nums[i]之后(得到减去后对应的“容量”)能有几种方法*1,因为是先遍历物品后遍历背包,所以不会有重复的地方。还有一点,初始化dp数组的时候,必须将dp[0]设置为1,虽然总和为0的方法数不一定是1(例如集合[0, 0 ,0]),即使初始化为1,最后也可能会增加,但如果初始化为0,dp数组就永远都是0(因为遍历背包是倒序遍历,而且除0外所有都初始化为0,所以如果该容量存在方法数,那么一定是从dp[0]开始加起来的,dp[0]=0,那加来加去都只能是0)。

值得一提的是首先要判断是否存在可行方案,如果得到的size不是整数(因为题目规定必须都是整数,所以加和不可能不是整数)(也就是看(target+sum)/2得到的背包容量是否是个整数,如果是个整数,那就意味着可能有解)或者干脆target大于sum的话,那就肯定没有解决方案了。

int findTargetSumWays(vector<int>& nums, int S) {
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) sum += nums[i];
        if (abs(S) > sum) return 0; // 此时没有方案
        if ((S + sum) % 2 == 1) return 0; // 此时没有方案
        int bagSize = (S + sum) / 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];
    }

474.一和零  

通过这道题目,大家先粗略了解, 01背包,完全背包,多重背包的区别,不过不用细扣,因为后面 对于 完全背包,多重背包 还有单独讲解。

视频讲解:动态规划之背包问题,装满这个背包最多用多少个物品?| LeetCode:474.一和零_哔哩哔哩_bilibili

代码随想录

首先明确这不是多重背包,而是01背包,每个字符串只能选取1次,但是是有两个维度的,所以必须使用二维数组来作为dp数组。

  1. 确定dp数组(dp table)以及下标的含义。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),和经典01背包的基本一致。
  3. dp数组初始化。首先dp[0][0]=0,这是肯定的,而其他的值也必须为0,否则取max没意义。
  4. 遍历顺序。这道题虽然是使用了二维数组,但是遍历顺序是和01背包使用一维数组作为dp数组时的遍历顺序是一致的,二维只是因为有两个维度而已,至于内部的两个循环(也就是哪个维度更先遍历的问题),顺序其实无所谓,两个维度是平等的。

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];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值