【算法练习Day36】最后一块石头的重量 II&&目标和&&一和零

这篇具有很好参考价值的文章主要介绍了【算法练习Day36】最后一块石头的重量 II&&目标和&&一和零。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【算法练习Day36】最后一块石头的重量 II&&目标和&&一和零,练题,算法

​📝个人主页:@Sherry的成长之路
🏠学习社区:Sherry的成长之路(个人社区)
📖专栏链接:练题
🎯长路漫漫浩浩,万事皆有期待

最后一块石头的重量 II

1049. 最后一块石头的重量 II - 力扣(LeetCode)
【算法练习Day36】最后一块石头的重量 II&&目标和&&一和零,练题,算法

最后一块石头的重量II,这道题是将各个不同重量的石头相互碰撞,碰撞规则是如果两石头重量一样,那么两块石头均会撞碎,不会有剩余石头。如果其中一颗石头重量较大,那么会剩下较大重量石头减去较轻石头的重量,也就是说剩下一个新重量的石头,题目要求我们返回尽量小的剩余石头重量。

那么如何尽量使剩余的石头的重量最小呢?我们要做的就是将石头重量大致的平分成两堆,让两堆石头相撞,如果两堆石头总重量相等,那么剩余重量为0,如果不等,那么剩余的重量仍然为最小。这就是我们解题的一个思路。

动规五部曲的分析:

dp数组的含义:当前能承载的重量下能够存储的石头最大重量,我们假设将这一堆石头重量平分为两堆,所以最大的重量容量也就是总石头重量的一半。

递推公式:递推公式也就是借用01背包的模型,将该块石头放入背包或者不放入背包共两种不同的状态。和01背包的递推公式完全一样,这里的重量和价值均为石头的重量。

dp数组初始化:dp【0】初始化为0,因为能装总重量为0的背包,装不进石头。剩下的不为0的背包初始化为什么呢?根据之前的01背包的一维数组的初始化可知,依然初始化为0,因为我们要比较dp【j】的本身,如果初始化过大,就会导致本来应该赋值的量并不能赋值上。不懂得可以去看01背包的详解。

遍历顺序:遍历顺序就是正常的从前遍历石头,先遍历那一颗石头并不影响结果,背包是从后向前遍历的,不懂依然是往前翻01背包的一维数组解决方法,大多数例题我们都用一维数组解决。

打印:在未得到理想的答案时候,我们要使用打印dp数组的方法来辅助分析。

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        vector<int>dp(1501,0);
        dp[0]=0;int sum=0;
        for(auto i:stones)sum+=i;
        int target=sum/2;
        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]-dp[target];
    }
};

和我们上面的分析相同的思路,也就是说如果五部曲能完全懂,那么代码也十分容易,数组我们可以给定1501的大小,这是根据题意所得到的,+1是为了避免数组越界。

我们使用的方法是把全部重量的石头分为两半,最大的背包就是其总和一半,最后我们返回的是两堆石头碰撞剩余的部分,总和减去一半再减一半,有人会问了为什么要这样写呢?因为如果石头总重量不能均分两半,那么一定是会有非0的剩余,由于总和/2是向下取整所以一定是取得较小的数,我们这样做减法得到的就是剩余的新石头的重量。

目标和

494. 目标和 - 力扣(LeetCode)

【算法练习Day36】最后一块石头的重量 II&&目标和&&一和零,练题,算法

目标和这道题我一看就丝毫没有思路,它有一点像是用回溯算法解决的题目,给定数组的每一个数都有取正和取负两种,然后最后算出共可以有多少次不同的方法凑得目标值。

但事实上是有可以用上01背包的思路的!思路有些不好想,具体思路是将要变成正数的部分和要变成负数的部分分开,正数部分加上那些应该是负数的部分,就可以得到目标值了。那么关键的来了,如何知道我们的左边正数部分应该取多少呢?它为多少时候我们才能加上右边部分凑成target呢?有以下公式推出,left-right=target这也就是说左边数字和右边数字相加就是目标值,为什么是减去呢?因为我们只是将右边看成是负数,其实并不是负数,我们将数组中正数逐个变成负数再放进负数堆里是很麻烦的,不是明智之举,所有我们直接减掉右边的正数就可以达成目的了。而left+right=sum也就是数组本身的数字逐个相加起来,那肯定就是总和sum了。这就可以推出right=sum-left,将该等式回带第一个式子得left=(sum+target)/2。这是个重要的式子,它帮助我们确定左边部分到底放多大,也就是背包最大容量是多大!sum可以求target是题目给的。

dp数组的含义:填满容量为j的背包共有多少种填充方法。

递推公式:递推公式的分析略显复杂,若j=5的情况下,给定数组{1,2,3,4,5},那么我们如果已经加入一个1,则需要dp【4】种方法凑成容量为5的背包,如果已经加入的数字是2,那么则需要dp【3】种方法才能够凑成容量为5的背包,以此类推如果加入的是数字5那么凑成容量为5背包需要dp【0】种,那么我们要求构成dp【5】共多少方法,也就是容量为5共多少种方法,那一定是前面的几种加在一起的和,则为构建的dp【5】的方法。就是dp【j-nums【i】】

而由于是累加在一起故递推公式是dp【j】+=dp【j-nums【i】】。

dp数组初始化:j为0时候初始化为1,这一点是和其他01背包题目初始化不同的地方,我们可以理解为目标值为0则有一种方法,不放入数组元素。事实上经过调试我们也可以知道,如果第一个位置初始化为0,那么累加的一直就是0了,无论target为多少,都无法出来其他数字了,所以一定要初始化为1。而其他j不等于0时候给它们初始化为0,这样方便第一次累加,实际上这和遍历顺序也有关联。

遍历顺序:遍历顺序是从左向右遍历物品,从后向前遍历背包,不懂看之前的文章。

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum=0;
        for(auto i:nums)sum+=i;
        if((target+sum)%2==1)return 0;
        int left=(target+sum)/2;
        if(abs(target)>sum)return 0;
        vector<int>dp(10001,0);dp[0]=1;
        for(int i=0;i<nums.size();i++){
            for(int j=left;j>=nums[i];j--){
                dp[j]+=dp[j-nums[i]];
            }
        }
        return dp[left];
    }
};

最后我们将dp【left】返回即可知道,我们为了凑成target共能有多少种方法。

一和零

474. 一和零 - 力扣(LeetCode)
【算法练习Day36】最后一块石头的重量 II&&目标和&&一和零,练题,算法

这道题又是01背包的一种新的应用题型。这道题乍一看也不像是能用到01背包特性的。这道题有两个维度m和n控制着结果的产生,这意味着我们必须使用二维数组来表示,这和其他题目用一维数组是一样的,其他题目是用一维数组代替二维数组,这道题实际上使用二维数组代替三维数组。

dp数组及其含义:dp【i】【j】i个0j个1的最大子集大小为多少,该背包的最大容量是dp【m】【n】。

递推公式:递推公式是和一维数组的递推公式很类似,dp【i】【j】=max(dp【i】【j】,dp【i-x】【j-y】+1)。其中的x和y分别代表遍历当前物品时候,物品所含有的0数量和1数量。当当前背包的i和j对应的小于了x或者y中的任何一个,则跳出。

dp数组的初始化:初始化全都是0,第一个位置初始化,0个0,0个1装入不了任何的子集。

遍历顺序:仍然是物品从左到右依次遍历,这里我们是一个一个物品取,并且再嵌套一个循环来分解该物品有多少个0和多少个1。分解完毕了,我们走遍历背包,遍历背包依然是从后向前,并且这道题的二维背包,无论是先遍历0还是先遍历1效果都是一样的,没有先后之分。

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<vector<int>>dp(m+1,vector<int>(n+1,0));
        for(string str:strs){
            int x=0,y=0;
            for(char ch:str){
                if(ch=='0')x++;
                else y++;}
                for(int i=m;i>=x;i--){
                    for(int j=n;j>=y;j--)
                    dp[i][j]=max(dp[i][j],dp[i-x][j-y]+1);
                }
            }  
            return dp[m][n];                                                     
        }
};

相当于持续的更新dp【m】【n】,遍历不同的物品,在最大背包的容量之内,我们尽可能地装入更多的物品,也就是拥有给定数组尽可能多的数据(子集)。这里还要对递推公式做进一步的解释,就是为什么我们dp【i-x】【j-y】之后还要有一个+1的操作?我们初始化本来都是0,这里递推公式就是比较当前容量最大应该是当前所包含子集多,还是倒退一次再装入的多,每次进行第二个运算都要+1,意义是加入一个子集,所以我们要使自己数量+1,这和dp数组的定义也是密切相关的,数组就表示了当前状态下的最大子集数量。

我们来做一个对于01背包这些题型的总结:
我们之前做的最开始01背包,是在当前的背包最大容量内能够携带的最大价值是多少。

分割等和子集这道题是给定背包容量,看能不能装满背包,如果不能装满说明不能分割成等和子集。

最后一块石头的重量II是给定背包容量,尽可能装,看能装多少,最后一做差,其实和分割等和子集有一点相像。

目标和是给定背包容量看有多少种装满的方法。

一和零是一个二维背包,给定背包容量看最多能装多少不同的物品。

这些都是01背包的不同维度上的解决问题的典例。其中以后两道题是有一些难度的。

总结:

这一期三道题目是对于01背包不同层次的应用,各有特色,也各有难点。同时也是01背包的最后一期,下一期我们来学习完全背包。希望我的文章和讲解能对大家的学习提供一些帮助。

当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~

【算法练习Day36】最后一块石头的重量 II&&目标和&&一和零,练题,算法文章来源地址https://www.toymoban.com/news/detail-740595.html

到了这里,关于【算法练习Day36】最后一块石头的重量 II&&目标和&&一和零的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Day43|动态规划part05: 1049. 最后一块石头的重量 II、494. 目标和、474. 一和零

    本题物品的重量为stones[i],物品的价值也为stones[i]。 对应着01背包里的物品重量weight[i]和 物品价值value[i]。 确定dp数组以及下标的含义 dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j] 。 确定递推公式 01背包的递推公式为:dp[j] = ma

    2024年04月23日
    浏览(48)
  • 算法训练第四十三天|1049. 最后一块石头的重量 II 、494. 目标和、474.一和零

    题目链接:1049. 最后一块石头的重量 II 参考:https://programmercarl.com/1049.%E6%9C%80%E5%90%8E%E4%B8%80%E5%9D%97%E7%9F%B3%E5%A4%B4%E7%9A%84%E9%87%8D%E9%87%8FII.html 题目难度:中等 有一堆石头,每块石头的重量都是正整数。 每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分

    2023年04月09日
    浏览(38)
  • 【LeetCode题目详解】第九章 动态规划 part05 1049. 最后一块石头的重量 II 494. 目标和 474.一和零(day43补)

    有一堆石头,用整数数组  stones 表示。其中  stones[i] 表示第 i 块石头的重量。 每一回合,从中选出 任意两块石头 ,然后将它们一起粉碎。假设石头的重量分别为  x 和  y ,且  x = y 。那么粉碎的可能结果如下: 如果  x == y ,那么两块石头都会被完全粉碎; 如果  x != y

    2024年02月09日
    浏览(38)
  • 【Day43】代码随想录之动态规划0-1背包_1049. 最后一块石头的重量 II_494. 目标和_ 474.一和零

    动态规划理论基础 动规五部曲: 确定dp数组 下标及dp[i] 的含义。 递推公式:比如斐波那契数列 dp[i] = dp[i-1] + dp[i-2]。 初始化dp数组。 确定遍历顺序:从前到后or其他。 打印。 出现结果不正确: 打印dp日志和自己想的一样:递推公式、初始化或者遍历顺序出错。 打印dp日志和

    2024年02月22日
    浏览(50)
  • leetcode 动态规划(最后一块石头的重量II、目标和、一和零)

    力扣题目链接(opens new window) 题目难度:中等 有一堆石头,每块石头的重量都是正整数。 每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x = y。那么粉碎的可能结果如下: 如果 x == y,那么两块石头都会被完全粉碎; 如果 x != y,那

    2024年02月03日
    浏览(47)
  • 【算法与数据结构】1049、LeetCode 最后一块石头的重量 II

    所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。    思路分析 :本题需要得到石头之间两两粉碎之后的最小值,那么一个简单的思路就是将这堆石头划分成大小相近的两小堆石头,然后粉碎,这样得到的结果必然是最优值。那么如何划分呢?我

    2024年01月21日
    浏览(48)
  • [Leetcode] 416. 分割等和子集、1049. 最后一块石头的重量 II、494. 目标和、474. 一和零

    内容:今天复习下dp数组中的背包问题 分割等和子集 - 能否装满 最后一块石头 - 尽可能装满 目标和 - 有多少种方法装 一和零 - 装满背包有多少个物品 416. 分割等和子集 10背包:用/不用;有容量;有价值 dp[j] : 容量为j,最大价值为dp[j]         重量和价值等价 dp[target] == t

    2024年02月16日
    浏览(43)
  • leetcode 1049. 最后一块石头的重量 II

             与分割等和子集类似,可以转化为0-1背包问题。 本题也是需要将数组元素分成两堆,区别在于本题需要使这两堆的差值最小,而之前那题是需要两堆差值为0。          使用之前的一维dp数组的思路,代码如下:

    2024年02月13日
    浏览(45)
  • LeetCode1049. 最后一块石头的重量 II

    一、题目 有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合,从中选出 任意两块石头 ,然后将它们一起粉碎。假设石头的重量分别为 x 和 y ,且 x = y 。那么粉碎的可能结果如下: 如果 x == y ,那么两块石头都会被完全粉碎; 如果 x != y ,

    2024年02月10日
    浏览(38)
  • Leetcode 1049 最后一块石头的重量II

    题意理解 :         有一堆石头,用整数数组  stones  表示。其中  stones[i]  表示第  i  块石头的重量。         每一回合,从中选出 任意两块石头 ,然后将它们一起粉碎。假设石头的重量分别为  x  和  y ,且  x = y 。         思路转化:我们可以将题目转换为

    2024年01月16日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包