力扣刷题day37|1049最后一块石头的重量 II、494目标和、474一和零

news2025/4/17 17:33:58

文章目录

    • 1049. 最后一块石头的重量 II
      • 思路
        • 动态规划五部曲
    • 494. 目标和
      • 回溯思路
      • 动态规划背包思路
        • 动态规划五部曲
    • 474. 一和零
      • 思路
        • 动态规划五部曲

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

力扣题目链接

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 xy,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

示例 1:

输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。

示例 2:

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

思路

本题其实就是尽量让石头分成重量相同的两堆,相撞之后剩下的石头最小,这样就化解成01背包问题了

本题物品的重量为stones[i],物品的价值也为stones[i]。

对应着01背包里的物品重量weight[i]和 物品价值value[i]。

动态规划五部曲

  1. 确定dp数组以及下标的含义

dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背dp[j]这么重的石头

  1. 确定递推公式

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]]为 容量为j - stones[i]的背包最大所背重量。

  1. dp数组如何初始化

们要求的target其实只是最大重量的一半,把石头遍历一遍,计算出石头总重量 然后除2,得到dp数组的大小。

因为重量都不会是负数,所以dp[j]都初始化为0就可以了,这样在递归公式dp[j] = max(dp[j], dp[j - stones[i]] + stones[i]);中dp[j]才不会初始值所覆盖。

  1. 确定遍历顺序

如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历

  1. 举例推导dp数组

image-20221102165945872

最后dp[target]里是容量为target的背包所能背的最大重量。

那么分成两堆石头,一堆石头的总重量是dp[target],另一堆就是sum - dp[target]。

在计算target的时候,target = sum / 2 因为是向下取整,所以sum - dp[target] 一定是大于等于dp[target]的

那么相撞之后剩下的最小石头重量就是 (sum - dp[target]) - dp[target]。

完整代码

public int lastStoneWeightII(int[] stones) {
    int sum = 0;
    for (int stone : stones) {
        sum += stone;
    }

    // 要求总重量的一半
    int target = sum / 2;
    int[] dp = new int[target + 1];
    for (int i = 0; i < stones.length; i++) {
        for (int j = target; j >= stones[i]; j--) {
            dp[j] = Math.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 的不同 表达式 的数目。

示例 1:

输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

示例 2:

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

回溯思路

这道题初看是用回溯法做,直接暴力搜索出所有情况。和39. 组合总和问题很相似。

动态规划背包思路

这道题目咋眼一看和动态规划背包啥的也没啥关系。

本题要如何使表达式结果为target

  • 如何转化为01背包问题呢?

假设原数组没有添加符号的所有正数和为 sum

假设添加 + 的总和为x,那么添加 - 对应的总和就是 sum - x。

所以题目的目标和是 x - (sum - x) = target

所以 x = (target + sum) / 2

此时问题就转化为,装满容量为x背包,有几种方法

看到(target + sum) / 2 应该担心计算的过程中向下取整有没有影响。

这么担心就对了,例如sum 是5,target是2的话其实就是无解的,所以:

if ((S + sum) % 2 == 1) return 0; // 此时没有方案

同时如果 target的绝对值已经大于sum(没有负号的情况下都达不到目标值),那么也是没有方案的。

if (Math.abs(S) > sum) return 0; // 此时没有方案

再回归到01背包问题,为什么是01背包呢?因为每个物品(题目中的1)只用一次!

这次和之前遇到的背包问题不一样了,之前都是求容量为j的背包,最多能装多少。

本题则是装满有几种方法。其实这就是一个组合问题了。

动态规划五部曲

  1. 确定dp数组以及下标的含义

dp[j] 表示:填满j(包括j)这么大容积的包(使和达到x),有dp[j]种方法

  1. 确定递推公式

不考虑nums[i]的情况下,填满容量为j的背包,有dp[j]种方法。

那么考虑nums[i]的话(只要搞到nums[i]),凑成dp[j]就有dp[j - nums[i]] 种方法。

例如:dp[j],j 为5,

  • 已经有一个1(nums[i]) 的话,有 dp[4]种方法 凑成 dp[5];
  • 已经有一个2(nums[i]) 的话,有 dp[3]种方法 凑成 dp[5];
  • 已经有一个3(nums[i]) 的话,有 dp[2]种方法 凑成 dp[5];
  • 已经有一个4(nums[i]) 的话,有 dp[1]种方法 凑成 dp[5];
  • 已经有一个5 (nums[i])的话,有 dp[0]种方法 凑成 dp[5]。

那么凑整dp[5]有多少方法呢,也就是把 所有的 dp[j - nums[i]] 累加起来。

所以求组合类问题的公式,都是类似这种:

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

这个公式在其他在背包解决排列组合问题的时候还会用到!

  1. dp数组如何初始化

从递归公式可以看出,在初始化的时候dp[0] 一定要初始化为1,因为dp[0]是在公式中一切递推结果的起源,如果dp[0]是0的话,递归结果将都是0。

dp[0] = 1,理论上也很好解释,装满容量为0的背包,有1种方法,就是装0件物品。

dp[j]其他下标对应的数值应该初始化为0,从递归公式也可以看出,dp[j]要保证是0的初始值,才能正确的由dp[j - nums[i]]推导出来。

  1. 确定遍历顺序

01背包问题一维dp的遍历,nums放在外循环,target在内循环,且内循环倒序。

  1. 举例推导dp数组

输入:nums: [1, 1, 1, 1, 1], target: 3

bagSize = (target + sum) / 2 = (3 + 5) / 2 = 4

image-20221102171324952

完整代码

public int findTargetSumWays(int[] nums, int target) {
    int sum = 0;
    for (int num : nums) {
        sum += num;
    }

    // 如果背包容量为奇数那就肯定不成立
    if ((target + sum) % 2 != 0) return 0;
    // 如果目标和绝对值超出sum
    if (Math.abs(target) > sum) return 0;

    // 背包j的大小
    int bagSize = (target + sum) / 2;
    // 如果添加正号的总和小于0,那也是不成立的
    if (bagSize < 0) return 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. 一和零

力扣题目链接

给你一个二进制字符串数组 strs 和两个整数 mn

请你找出并返回 strs 的最大子集的长度,该子集中 最多m0n1

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y子集

示例 1:

输入:strs = ["10", "0001", "111001", "1", "0"], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {"10","0001","1","0"} ,因此答案是 4 。
其他满足题意但较小的子集包括 {"0001","1"} 和 {"10","1","0"} 。{"111001"} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。

示例 2:

输入:strs = ["10", "0", "1"], m = 1, n = 1
输出:2
解释:最大的子集是 {"0", "1"} ,所以答案是 2 。

思路

多重背包是每个物品,数量不同的情况。

**本题中strs 数组里的元素就是物品,每个物品都是一个!**所以是

而m 和 n相当于是一个背包,两个维度的背包

但本题其实是01背包问题。不过这个背包有两个维度,一个是m 一个是n,而不同长度的字符串就是不同大小的待装物品。

动态规划五部曲

  1. 确定dp数组以及下标的含义

dp[i] [j]:最多有i个0和j个1的strs的最大子集的大小为dp[i] [j]

  1. 确定递推公式

dp[i][j] 可以由前一个strs里的字符串推导出来,strs里的字符串有zeroNum个0,oneNum个1。

当前有i个0和j个1的子集大小为dp[i] [j],前一个状态就是去掉当前0的个数zeroNum和1的个数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])

  1. dp数组如何初始化

01背包的dp数组初始化为0就可以。

因为物品价值不会是负数,初始为0,保证递推的时候dp[i][j]不会被初始值覆盖。

  1. 确定遍历顺序

对有两个维度的背包也是从后往前遍历。

本题中物品就是strs里的字符串,背包容量就是题目描述中的m和n。因此先遍历物品在遍历背包

  1. 举例推导dp数组

以输入:[“10”,“0001”,“111001”,“1”,“0”],m = 3,n = 3为例

image-20221102213817211

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

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

相关文章

深度学习模型部署全流程-模型训练

文章目录前言模型训练全流程1.数据准备2.数据加载3.搭建神经网络4.设置损失函数&#xff0c;优化器5.训练网络模型6.模型测试7.完整代码9.训练结果小结前言 该系列文章会介绍神经网络模型从训练到部署的全流程&#xff0c;对于已经参加工作的人可以快速的了解如何使用深度学习…

Android Studio入门之文本内容、大小、颜色的讲解及实战(附源码 超详细必看)

运行有问题或需要源码请点赞关注收藏后评论区留言或私信博主 一、设置文本的内容 1:在XML文件中通过属性android:text设置文本 <TextViewandroid:layout_width"wrap_content"android:layout_height"wrap_content"android:text"Hello World!"…

nordic 52832中添加RTT打印

JlinkRTT RTT是基于Jlink调试器的实时传输技术,可以代替串口打印一些调试信息,不需要额外接线。 nordic 52832官方例程中,会将RTT打印函数做进一步封装,下面就讲一下怎么开启52832中的RTT打印。 第一步 增加RTT代码 RTT源代码可以在segger官方网站下载,也可以在nordic 5…

使用 stream buffer 传递数据

使用 stream buffer 传递数据 概述 如前所述&#xff0c;队列虽然提供了任务之间传递数据的功能&#xff0c;但没有对通知机制进行优化&#xff0c;即不方便实现多次采集不同长度的数据&#xff0c;然后触发一次通知接收的机制。 特性概述 Streambuffer 的中文含意是“流式…

Chapter5.5:频率响应法

此系列属于胡寿松《自动控制原理题海与考研指导》(第三版)习题精选&#xff0c;仅包含部分经典习题&#xff0c;需要完整版习题答案请自行查找&#xff0c;本系列属于知识点巩固部分&#xff0c;搭配如下几个系列进行学习&#xff0c;可用于期末考试和考研复习。 自动控制原理(…

Hive与Hbase的区别与联系

一、概念 1&#xff0c;Hive hive是基于Hadoop的一个数据仓库工具&#xff0c;用来进行数据提取、转化、加载&#xff0c;这是一种可以存储、查询和分析存储在Hadoop中的大规模数据的机制。hive数据仓库工具能将结构化的数据文件映射为一张数据库表&#xff0c;并提供SQL查询…

网站中的经典,分享那些我用过的宝藏网站

前言 本篇将会具体分享我在最开始学习编程时了解到的网站&#xff0c;并分享自己使用这些网站的感受&#xff0c;当然&#xff0c;如果我有说的不正确的或者需要补充的&#xff0c;欢迎评论区补充纠正。还有各位来自优秀学校的伙伴们&#xff0c;或许其中一些资源在你们的学校…

安卓开发Android studio学习笔记15:关于如何使用Okhttp框架的网络请求(调用API接口)

Android studio一、安卓基于HTTP网络编程(一)、两种请求方式(二&#xff09;、安卓基于HTTP网络编程的两种方式1、使用HttpURLConnection访问网络资源**2、利用HttpClient访问网络资源**&#xff08;1&#xff09;HttpGet&#xff08;2&#xff09;HttpPost二、基础Okhttp的网络…

修改 echarts 默认样式记录

1、修改折线图上的数据标记点 showSymbol:false , 表示不展示数据点&#xff0c;只有鼠标 hover 时&#xff0c; tooltip 展示。 series: [{name: 进场, // 名称&#xff0c;图例和 tooltip 中展示showSymbol: false, // 不展示数据标记点type: line, // 类型color: #0091FF…

大学解惑10 - CSS中的content怎么换行,以及使用before伪类的优点

大学解惑09 - 单独用HTML javascript CSS 实现三版99乘法表&#xff0c;你就是班里最靓的仔https://blog.csdn.net/xingyu_qie/article/details/127631612 ☆ 上一篇文章用前端HTML CSS JS基础写了3版99乘法表&#xff0c;有同学说终于把99乘法表写透了&#xff0c;但是紧接着就…

Linux关于JDK、Tomcat以及MySQL安装

目录 一、JDK安装 1、 上传jdk、tomcat安装包 2、解压两个工具包 3、配置环境 4、在配置文件中加入java环境变量&#xff1a; 5、保存&#xff0c;让新设置的环境变量生效 二、Tomcat安装 1、将tomcat解压到/opt下 2、配置环境变量 3、启动tomcat 4、创建启动脚本 三…

入门学习XSS漏洞,这一篇就够了

入门学习XSS漏洞&#xff0c;这一篇就够了1.XSS简介2.XSS的类型反射型XSS存储型XSSDOM型XSS1.XSS简介 XSS攻击&#xff0c;通常指黑客通过“HTML注入”篡改了网页&#xff0c;插入了恶意的脚本&#xff0c;从而在用户浏览网页时&#xff0c;控制用户浏览器的一种攻击。在一开始…

【沐风老师】怎么在3DMAX中使用MAXScript脚本动画编程?

大家可能对3dmax都抱有很浓厚的兴趣,但如果你接触到max脚本(MAXScript),你会觉得它比max本身更让人着迷,因为它更能拓展我们的想象力,或者帮助我们更好的提高工作效率。不过,MAXScript是解释语言,不适合编写过于复杂的功能,因为这将大大影响执行的速度。 言归正传,就…

jmeter模拟多IP访问

1. 前言&#xff1a; 今天一同事在压测时提到怎么用jmeter里虚拟多个ip来发送请求&#xff0c;我想了一下以前用LR时用过虚拟ip地址&#xff0c;jmeter还没有使用过。想着原理应该是相通的&#xff0c;既然LR都能支持的话&#xff0c;那Jmeter应该也是支持&#xff0c;于是就有…

ARM pwn 入门 (1)

最近笔者刚刚加入了一个项目组&#xff0c;需要用到ARM架构的东西&#xff0c;和ARM pwn也有一定关系&#xff0c;因此一不做二不休&#xff0c;决定开始学习ARM pwn&#xff0c;顺便熟悉项目前置知识&#xff0c;一举两得。 ARM与x86分属不同架构&#xff0c;指令集不同&…

用frp搞个内网穿透

使用场景&#xff1a; 在公司用电脑敲代码&#xff0c;环境都是localhost&#xff0c;有时候你要接第三方接口比如支付、或者企业微信的事件回调等&#xff0c;都需要一个公网地址&#xff0c;因为这时候是开发阶段&#xff0c;你即想要公司电脑上运行的环境又想要回调能找到你…

2022年首家民营征信机构浙江同信获企业征信备案公示

2022年首家民营征信机构浙江同信获企业征信备案公示 2022年11月1日&#xff0c;中国人民银行杭州中心支行公示了浙江同信企业征信服务有限公司企业征信机构备案&#xff0c;该机构为浙江省进行备案公示的第九家机构。其他八家分别为芝麻信用管理有限公司、浙江有数数智科技有限…

Transform介绍(1)

文章目录1. transform 方法2. transform 增量模式3. 注册 Transform使用Transform的常见场景有埋点统计、耗时监控、方法替换 通过上图以我们了解下transform的作用&#xff0c;transform在 class 到 dex 之间工作&#xff0c;处理包括 javac 编译后的字节码文件&#xff0c;每…

【Linux内核系列】进程调度

目录 一、为什么要调度 二、调度均衡 三、进程调度框架 3.1 调度队列 3.2 进程唤醒 3.3 调度时机 主动调度&#xff1a; 被动调度&#xff1a; 四、调度算法 4.1 先来先服务调度算法 4.2 最短作业优先调度算法 4.3 高响应比优先调度算法 4.4 时间片轮转调度算法 …

洛谷千题详解 | P1007 独木桥【C++、Pascal语言】

博主主页&#xff1a;Yu仙笙 专栏地址&#xff1a;洛谷千题详解 目录 题目背景 题目描述 输入格式 输出格式 输入输出样例 解析&#xff1a; C源码&#xff1a; Pascal源码&#xff1a; ------------------------------------------------------------------------------------…