代码随想录算法训练营第四十三天 | 1049. 最后一块石头的重量 II,494. 目标和,474.一和零

news2025/1/8 5:42:20

代码随想录算法训练营第四十三天 | 1049. 最后一块石头的重量 II,494. 目标和,474.一和零

  • 1049. 最后一块石头的重量 II
  • 494. 目标和
    • 回溯算法
    • 动态规划
  • 474.一和零

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

题目链接
视频讲解
有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量,每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y,那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x
最后,最多只会剩下一块 石头,返回此石头 最小的可能重量,如果没有石头剩下,就返回 0

输入:stones = [31,26,33,21,40]
输出:5

本题物品的重量为stones[i],物品的价值也为stones[i],对应着01背包里的物品重量weight[i]和 物品价值value[i]
接下来进行动规五步曲:
确定dp数组以及下标的含义
dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j],可以回忆一下01背包中,dp[j]的含义,容量为j的背包,最多可以装的价值为 dp[j],相对于 01背包,本题中,石头的重量是 stones[i],石头的价值也是 stones[i] ,可以 “最多可以装的价值为 dp[j]” == “最多可以背的重量为dp[j]”
确定递推公式
01背包的递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);本题则是:dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);一些人可能看到这dp[j - stones[i]] + stones[i]中 又有- stones[i] 又有+stones[i],看着有点晕乎,可以再去看 dp[j]的含义
dp数组如何初始化
既然 dp[j]中的j表示容量,那么最大容量(重量)是多少呢,就是所有石头的重量和,因为提示中给出1 <= stones.length <= 30,1 <= stones[i] <= 1000,所以最大重量就是30 * 1000 ,而我们要求的target其实只是最大重量的一半,所以dp数组开到15000大小就可以了,当然也可以把石头遍历一遍,计算出石头总重量 然后除2,得到dp数组的大小,接下来就是如何初始化dp[j]呢,因为重量都不会是负数,所以dp[j]都初始化为0就可以了,这样在递归公式dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);中dp[j]才不会初始值所覆盖
代码为:

vector<int> dp(15001, 0);

确定遍历顺序
在动态规划:关于01背包问题,你该了解这些!(滚动数组) (opens new window)中就已经说明:如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!
代码如下:

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]);
    }
}

举例推导dp数组
举例,输入:[2,4,1,1],此时target = (2 + 4 + 1 + 1)/2 = 4 ,dp数组状态图如下:
在这里插入图片描述
最后dp[target]里是容量为target的背包所能背的最大重量,那么分成两堆石头,一堆石头的总重量是dp[target],另一堆就是sum - dp[target],在计算target的时候,target = sum / 2 因为是向下取整,所以sum - dp[target] 一定是大于等于dp[target]的,那么相撞之后剩下的最小石头重量就是 (sum - dp[target]) - dp[target]

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

494. 目标和

题目链接
视频讲解
给你一个非负整数数组 nums 和一个整数 target ,向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1”
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目

输入:nums = [1,1,1,1,1], target = 3
输出:5

本题要如何使表达式结果为target,既然为target,那么就一定有 left组合 - right组合 = target,left + right = sum,而sum是固定的,right = sum - left,公式来了, left - (sum - left) = target 推导出 left = (target + sum)/2 ,target是固定的,sum是固定的,left就可以求出来,此时问题就是在集合nums中找出和为left的组合

回溯算法

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {
        if (sum == target) {
            result.push_back(path);
        }
        // 如果 sum + candidates[i] > target 就终止遍历
        for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {
            sum += candidates[i];
            path.push_back(candidates[i]);
            backtracking(candidates, target, sum, i + 1);
            sum -= candidates[i];
            path.pop_back();

        }
    }
public:
    int findTargetSumWays(vector<int>& nums, int S) {
        int sum = 0;
        for (int i = 0; i < nums.size(); i++) sum += nums[i];
        if (S > sum) return 0; // 此时没有方案
        if ((S + sum) % 2) return 0; // 此时没有方案,两个int相加的时候要各位小心数值溢出的问题
        int bagSize = (S + sum) / 2; // 转变为组合总和问题,bagsize就是要求的和

        // 以下为回溯法代码
        result.clear();
        path.clear();
        sort(nums.begin(), nums.end()); // 需要排序
        backtracking(nums, bagSize, 0, 0);
        return result.size();
    }
};

动态规划

如何转化为01背包问题呢,假设加法的总和为x,那么减法对应的总和就是sum - x,所以我们要求的是 x - (sum - x) = target,x = (target + sum) / 2,此时问题就转化为,装满容量为x的背包,有几种方法,这里的x,就是bagSize,也就是我们后面要求的背包容量,大家看到(target + sum) / 2 应该担心计算的过程中向下取整有没有影响,这么担心就对了,例如sum 是5,S是2的话其实就是无解的,所以:

(C++代码中,输入的S 就是题目描述的 target)
if ((S + sum) % 2 == 1) return 0; // 此时没有方案

同时如果 S的绝对值已经大于sum,那么也是没有方案的

(C++代码中,输入的S 就是题目描述的 target)
if (abs(S) > sum) return 0; // 此时没有方案

再回归到01背包问题,为什么是01背包呢?因为每个物品(题目中的1)只用一次!这次和之前遇到的背包问题不一样了,之前都是求容量为j的背包,最多能装多少,本题则是装满有几种方法。其实这就是一个组合问题了
确定dp数组以及下标的含义
dp[j] 表示:填满j(包括j)这么大容积的包,有dp[j]种方法,其实也可以使用二维dp数组来求解本题,dp[i][j]:使用 下标为[0, i]的nums[i]能够凑满j(包括j)这么大容量的包,有dp[i][j]种方法,下面我都是统一使用一维数组进行, 二维降为一维(滚动数组),其实就是上一层拷贝下来
确定递推公式
有哪些来源可以推出dp[j]呢?只要搞到nums[i],凑成dp[j]就有dp[j - nums[i]] 种方法
例如:dp[j],j 为5
已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 容量为5的背包
已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 容量为5的背包
已经有一个3(nums[i]) 的话,有 dp[2]中方法 凑成 容量为5的背包
已经有一个4(nums[i]) 的话,有 dp[1]中方法 凑成 容量为5的背包
已经有一个5 (nums[i])的话,有 dp[0]中方法 凑成 容量为5的背包
那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来
所以求组合类问题的公式,都是类似这种:

dp[j] += dp[j - nums[i]]

这个公式在后面在讲解背包解决排列组合问题的时候还会用到!
dp数组如何初始化
从递推公式可以看出,在初始化的时候dp[0] 一定要初始化为1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递推结果将都是0,如果数组[0] ,target = 0,那么 bagSize = (target + sum) / 2 = 0。 dp[0]也应该是1, 也就是说给数组里的元素 0 前面无论放加法还是减法,都是 1 种方法,所以本题我们应该初始化 dp[0] 为 1,那 如果是 数组[0,0,0,0,0] target = 0 呢,其实 此时最终的dp[0] = 32,也就是这五个零 子集的所有组合情况,但此dp[0]非彼dp[0],dp[0]能算出32,其基础是因为dp[0] = 1 累加起来的,dp[j]其他下标对应的数值也应该初始化为0,从递推公式也可以看出,dp[j]要保证是0的初始值,才能正确的由dp[j - nums[i]]推导出来
确定遍历顺序
在动态规划:关于01背包问题,你该了解这些!(滚动数组)中,我们讲过对于01背包问题一维dp的遍历,nums放在外循环,target在内循环,且内循环倒序
举例推导dp数组
输入:nums: [1, 1, 1, 1, 1], S: 3
bagSize = (S + sum) / 2 = (3 + 5) / 2 = 4
dp数组状态变化如下:
在这里插入图片描述

class Solution {
public:
    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.一和零

题目链接
视频讲解
给你一个二进制字符串数组 strs 和两个整数 m 和 n,请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1,如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的子集

输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4

多重背包是每个物品,数量不同的情况,本题中strs 数组里的元素就是物品,每个物品都是一个!而m 和 n相当于是一个背包,两个维度的背包,理解成多重背包主要是把m和n混淆为物品了,感觉这是不同数量的物品,所以以为是多重背包,但本题其实是01背包问题!只不过这个背包有两个维度,一个是m 一个是n,而不同长度的字符串就是不同大小的待装物品
动规五部曲:
确定dp数组(dp table)以及下标的含义
dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]
确定递推公式
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);此时可以回想一下01背包的递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);对比一下就会发现,字符串的zeroNum和oneNum相当于物品的重量(weight[i]),字符串本身的个数相当于物品的价值(value[i]),这就是一个典型的01背包! 只不过物品的重量有了两个维度而已
dp数组如何初始化
在动态规划:关于01背包问题,你该了解这些!(滚动数组)中已经讲解了,01背包的dp数组初始化为0就可以,因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖
确定遍历顺序
在动态规划:关于01背包问题,你该了解这些!(滚动数组)中,我们讲到了01背包为什么一定是外层for循环遍历物品,内层for循环遍历背包容量且从后向前遍历!那么本题也是,物品就是strs里的字符串,背包容量就是题目描述中的m和n

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);
        }
    }
}

那个遍历背包容量的两层for循环先后循序有没有什么讲究?没讲究,都是物品重量的一个维度,先遍历哪个都行
举例推导dp数组
以输入:[“10”,“0001”,“111001”,“1”,“0”],m = 3,n = 3为例
最后dp数组的状态如下所示:
在这里插入图片描述

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];
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/918331.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

API接口变更频繁,协作效率低?试试Apipost

在团队协作开发和API管理过程中&#xff0c;API接口变更频繁、变更信息得不到及时同步且同步后沟通效率低&#xff0c;各个环节人员处理问题节奏不统一等等&#xff0c;都是在API开发中很多开发者面临的痛点。 这些痛点可能会导致工作进度受阻、出现重复动作等多个问题&#x…

化繁为简,使用Hibernate Validator实现参数校验(一)

目录 前言 环境配置 导入依赖 基础校验 校验注解 参数绑定 PathVariable RequestParam RequestBody Validated Valid 单参校验 对象校验 分组校验 顺序校验 前言 在之前的悦享校园的开发中使用了SSM框架&#xff0c;由于当时并没有使用参数参数校验工具&#xf…

如何向学校图书馆推荐数据库?

学校图书馆在满足师生学习和研究需求方面起着至关重要的作用。作为一名学生或研究员&#xff0c;您可能会意识到某些数据库对于学校的教学和研究至关重要。遇到拥有优质数据的数据库如何向学校图书馆推荐呢&#xff1f; 一、了解学校需求。 在向学校图书馆推荐数据库之前&…

WMS精细化库存管理

在现代供应链管理中&#xff0c;仓储和库存管理起着至关重要的作用。仓库管理系统&#xff08;WMS&#xff09;作为一种关键工具&#xff0c;通过精细化库存管理&#xff0c;提高了仓库操作的效率和准确性。 一、概述WMS精细化库存管理 WMS精细化库存管理是指利用先进的信息技…

【1++的数据结构】之map与set(一)

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的数据结构】 文章目录 一&#xff0c;关联式容器与键值对二&#xff0c;setset的使用 三&#xff0c;mapmap的使用 四&#xff0c;multiset与multimap 一&#xff0c;关联式容器与键值对 像l…

linux文本三剑客

linux文本三剑客 1、grep2、sed 1、grep 过滤 参数用法作用-igrep -i STRING xxx.txt从xxx.txt文件查找不区分大小写STRING-wgrep -w STRING xxx.txt精确匹配STRING-egrep -e STRING1 -e STRING2 xxx.txt查找多个STRING行-ngrep -n STRING xxx.txt查看STRING 在第几行-vgrep …

亚马逊秒杀有什么作用

亚马逊的秒杀活动具有多方面的作用&#xff0c;对于消费者、亚马逊自身以及卖家来说都有一定的影响和作用&#xff1a; 1、吸引消费者和增加销售额&#xff1a;秒杀活动的低价优惠通常会吸引大量的消费者&#xff0c;促使他们在限定的时间内购买商品。这有助于增加销售额和订单…

React+Typescript从请求数据到列表渲染

我们在项目src目录下创建一个目录 叫 pages 在里面创建一个组件叫 list.tsx 这里 我启动了自己的java项目 创建接口 你们就也需要弄几个自己的接口做测试 然后 list.tsx 编写代码如下 import * as React from "react";export default class hello extends React.C…

空调企业只干空调,卡萨帝却干了业主想不到的事

作者 | 曾响铃 文 | 响铃说 今年入夏以来&#xff0c;随着气温的不断攀升&#xff0c;空调已经成为生活、工作中的“绝对刚需”。由此而来的则是空调产品的销量大增。 但也有不少消费者表示&#xff0c;随着产品功能的越发相似&#xff0c;价格趋同&#xff0c;使空调变得越…

前端显示gif流文件,gif图验证码

1、前端界面展示效果图&#xff1a;gif动态图片 2、接口获取流文件 3、接口配置 export function getBlob(params){return request({url: /session/generatorCode,method: get,params,catchAll:true,responseType:"arraybuffer"}) }4、数据处理 getBlob({key:thi…

【微服务】spring 条件注解从使用到源码分析详解

目录 一、前言 二、spring 条件注解概述 2.1 条件注解Conditional介绍 2.2 Conditional扩展注解 2.2.1 Conditional扩展注解汇总 三、spring 条件注解案例演示 3.1 ConditionalOnBean 3.2 ConditionalOnMissingBean 3.2.1 使用在类上 3.2.2 使用场景补充 3.3 Condit…

The last packet sent successfully to the server was 0 milliseconds ago.报错

报错图&#xff1a; 解决方法 多半是代码拼写错误&#xff0c;如localhost拼成local。 注意驱动版本应与mysql版本对应 maven包配置按照自己下的位置设置&#xff0c;最好不要默认

成都车展亮相:专为亲子家庭打造,极狐考拉纯电智能亲子车

极狐汽车旗下的考拉品牌于今年年初面向拥有婴幼儿的中产阶级宝爸宝妈推出&#xff0c;旨在满足他们对于安全、健康和便利的高要求。这款名为“考拉”的纯电智能亲子车将于2023年8月25日在成都车展上开启预售。 外观方面&#xff0c;考拉的造型采用流线型的车身设计&#xff0c;…

[网络架构]Self-organized operational neural networks (SelfONN)

Self-organized operational neural networks (SelfONN 背景CNNONNSelfONNCNN, ONN&#xff0c; SelfONN对比SelfONN与CNN的关系总结References 背景 本节要分享的是SelfONN, SelfONN可以看作是ONN的优化/升级&#xff0c; 而ONN可以看作是更一般化的CNN&#xff0c; 克服了CN…

什么是神经网络

什么是神经网络 什么是神经网络&#xff1f;CNN、RNN、GNN&#xff0c;这么多的神经网络&#xff0c;有什么区别和联系&#xff1f; 既然我们的目标是打造人工智能&#xff0c;拥有智慧的大脑无疑是最好的模仿对象&#xff0c;人脑中有约860亿个神经元&#xff0c;这被认为是…

运维高级学习--Docker(二)

1、使用mysql:5.6和 owncloud 镜像&#xff0c;构建一个个人网盘。 #拉取mysql5.6和owncloud镜像 [rootlocalhost ~]# docker pull mysql:5.6 [rootlocalhost ~]# docker pull owncloud [rootlocalhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED …

【FAQ】H.265视频无插件流媒体播放器EasyPlayer.js播放webrtc断流重连的异常修复

H5无插件流媒体播放器EasyPlayer属于一款高效、精炼、稳定且免费的流媒体播放器&#xff0c;可支持多种流媒体协议播放&#xff0c;可支持H.264与H.265编码格式&#xff0c;性能稳定、播放流畅&#xff0c;能支持WebSocket-FLV、HTTP-FLV&#xff0c;HLS&#xff08;m3u8&#…

Rider 添加NuGet软件包 (NuGet Package)

如图&#xff0c;在解决方案中选择自己的项目右键&#xff0c;点击管理 NuGet 软件包即可 在搜索栏中搜索自己要使用的软件包安装即可使用

K8S如何部署Redis(单机、集群)

在今天的讨论中&#xff0c;我们将深入研究如何将Redis数据库迁移到云端&#xff0c;以便更好地利用云计算的优势提高数据管理的灵活性。 Redis(Remote Dictionary Server)是一个开源的、基于内存的数据结构存储系统&#xff0c;它可以用作数据库、缓存和消息代理。Redis支持多…

MES生产报工管理

一、MES生产报工管理的定义与功能&#xff1a; MES生产报工管理是指利用制造执行系统&#xff08;MES&#xff09;对生产过程进行实时监控、数据采集和分析&#xff0c;并及时记录和报告生产工单的实际完成情况。其主要功能包括&#xff1a; 1. 实时数据采集&#xff1a;通过…