我的 LeetCode 日记:Day 36 - 动态规划,背包问题的千变万化

昨天,我初步掌握了 0/1 背包问题的理论基础和标准解法。今天,我将这种思想应用到了更广泛的场景中。今天的几道题,乍一看和背包没什么关系,但通过巧妙的数学转化,它们的核心都变成了 0/1 背包问题。

这让我深刻体会到,学习算法不仅仅是学习模板,更重要的是学习一种建模能力——将一个看似全新的问题,转化为我们熟悉的模型来求解。为了加深理解,我对每个适用的问题都同时实现了二维和一维的解法,以观察它们之间的联系和区别。

一、实战演练:识别伪装的背包问题

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

题目描述: 有一堆石头,用整数数组 stones 表示。其中 stones[i] 是第 i 块石头的重量。每一回合,从中选出任意两块石头,然后将它们一起粉碎…最后,最多只会剩下一块石头。返回此石头最小的可能重量。

  • 学习感悟: 这个问题等价于将所有石头分成两堆 AB,并让它们的重量差 |sum(A) - sum(B)| 最小。为了实现这一点,我们需要让其中一堆的重量 sum(A) 尽可能地接近总重量的一半。这完美地转化为了一个 0/1 背包问题:

    • 背包容量: target = total_sum // 2
    • 物品: stones 数组中的每一个 stone
    • 物品重量/价值: stone 的重量
    • 目标: 在容量为 target 的背包里,能装下的最大重量是多少?
      在这里插入图片描述
  • 资源包:
    * 题目/文章: 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
    * 视频讲解: https://www.bilibili.com/video/BV14M411C7oV

我的实现 1:二维 DP
class Solution:def lastStoneWeightII(self, stones: List[int]) -> int:total_sum = sum(stones)target = total_sum // 2n = len(stones)# dp[i][j]: 从0..i-1号石头中任选,放入容量j的背包,能达到的最大重量dp = [[0] * (target + 1) for _ in range(n + 1)]for i in range(1, n + 1):stone_weight = stones[i - 1]for j in range(target + 1):if j < stone_weight:dp[i][j] = dp[i - 1][j] # 装不下,不装else:# 装或者不装dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - stone_weight] + stone_weight)sum_A = dp[n][target]sum_B = total_sum - sum_Areturn sum_B - sum_A
我的实现 2:一维 DP (空间优化)
class Solution:def lastStoneWeightII(self, stones: List[int]) -> int:total_sum = sum(stones)target = total_sum // 2# dp[j]:容量为j的背包,能装下的最大重量dp = [0] * (target + 1)for stone_weight in stones:# 倒序遍历,保证每个石头只用一次for j in range(target, stone_weight - 1, -1):dp[j] = max(dp[j], stone_weight + dp[j - stone_weight])sum_A = dp[target]sum_B = total_sum - sum_Areturn sum_B - sum_A
2. LeetCode 494. 目标和

题目描述: 给你一个整数数组 nums 和一个整数 target 。向数组中的每个整数前添加 '+''-'。返回可以通过上述方法构造的、运算结果等于 target 的不同表达式的数目。

  • 学习感悟: 这道题是计数型 DP,但同样可以转化为背包问题。假设加 + 的子集和为 P,加 - 的子集和为 N。我们有 P - N = targetP + N = total_sum。两式相加得 2P = target + total_sum,即 P = (target + total_sum) / 2
    • 背包模型: 问题变成了:从 nums 中挑选出一些数,使其和恰好为 new_target = (target + total_sum) / 2,共有多少种挑法?
    • DP 定义: dp[j] 表示和为 j 的组合有多少种。
  • 资源包:
    • 题目/文章: https://programmercarl.com/0494.%E7%9B%AE%E6%A0%87%E5%92%8C.html
    • 视频讲解: https://www.bilibili.com/video/BV1o8411j73x
我的实现 1:二维 DP
class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:total_sum = sum(nums)if abs(target) > total_sum or (total_sum + target) % 2 != 0:return 0new_target = (total_sum + target) // 2n = len(nums)# dp[i][j]: 从0..i-1号数中任选,和为j的组合数dp = [[0] * (new_target + 1) for _ in range(n + 1)]dp[0][0] = 1 # 用0个数凑成和为0,有一种方法(什么都不选)for i in range(1, n + 1):num = nums[i - 1]for j in range(new_target + 1):if j < num:dp[i][j] = dp[i - 1][j] # 不选numelse:# 组合数 = (不选num的方法数) + (选num的方法数)dp[i][j] = dp[i - 1][j] + dp[i - 1][j - num]return dp[n][new_target]
我的实现 2:一维 DP (空间优化)
class Solution:def findTargetSumWays(self, nums: List[int], target: int) -> int:total_sum = sum(nums)if abs(target) > total_sum or (total_sum + target) % 2 != 0:return 0new_target = (total_sum + target) // 2# dp[j]:和为 j 的组合有多少种dp = [0] * (new_target + 1)dp[0] = 1for num in nums:# 倒序遍历,保证每个数只用一次for j in range(new_target, num - 1, -1):dp[j] += dp[j - num]return dp[new_target]
3. LeetCode 474. 一和零

题目描述: 给你一个二进制字符串数组 strs 和两个整数 mn 。请你找出 strs 的最大子集的长度,该子集中 最多m0n1

  • 学习感悟: 这道题是二维费用的 0/1 背包,是背包问题的一个重要变种。

    • 背包容量: 是一个二维的 (m, n),分别代表 01 的容量。
    • 物品: strs 数组中的每个字符串。
    • 物品重量: 每个字符串的重量也是二维的 (count_zeros, count_ones)
    • 物品价值: 每个字符串价值都是 1
  • DP 定义: dp[i][j] 表示用 i0j1 能构成的最大子集长度。

  • 状态转移: dp[i][j] = max(dp[i][j], 1 + dp[i - count_zeros][j - count_ones])

  • 遍历顺序: 因为是 0/1 背包,两个表示容量的循环 ij必须倒序遍历

  • 资源包:

    • 题目/文章: https://programmercarl.com/0474.%E4%B8%80%E5%92%8C%E9%9B%B6.html
    • 视频讲解: https://www.bilibili.com/video/BV1rW4y1x7ZQ
我的实现:二维费用背包
class Solution:def findMaxForm(self, strs: List[str], m: int, n: int) -> int:# dp[i][j]:用 i 个 0 和 j 个 1 能构成的最大子集长度dp = [[0] * (n + 1) for _ in range(m + 1)]for s in strs: # 遍历物品count_zeros = s.count('0')count_ones = s.count('1')# 倒序遍历背包的两个维度容量for i in range(m, count_zeros - 1, -1):for j in range(n, count_ones - 1, -1):dp[i][j] = max(dp[i][j], 1 + dp[i - count_zeros][j - count_ones])return dp[m][n]

总结

今天我更深刻地理解了 0/1 背包问题的泛用性。它不仅仅是一个求最大价值的模型,通过改变 dp 数组的定义(求最大重量、求组合数、求可行性)和状态转移的方式,它可以解决各种各样的问题。识别问题背后的背包模型,并正确地进行转化,是解决这类 DP 问题的关键所在。从一维费用到二维费用,背包问题的世界真是广阔。

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

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

相关文章

本地处理不上传!隐私安全的PDF转换解决方案

PDF能锁定排版、字体、图片位置&#xff0c;无论在什么设备打开都保持一致。它是无广告、简洁高效的专业PDF处理工具。功能丰富&#xff0c;支持批量操作&#xff1a;只需将文件拖入界面&#xff0c;选择目标格式&#xff08;如Word、PPT、Excel、图片等&#xff09;&#xff0…

Docker build创建镜像命令入门教程

一、核心概念Dockerfile 定义镜像构建步骤的文本文件&#xff0c;包含一系列指令和配置&#xff0c;用于自动化创建镜像。镜像层&#xff08;Layer&#xff09; Docker 镜像由多层只读层叠加而成&#xff0c;每个指令&#xff08;如 RUN、COPY&#xff09;会生成一个新的层。层…

Redis 是单线程模型吗?

最近在面试中经常被问到这个问题&#xff1a;"Redis是单线程的吗&#xff1f;"很多同学都会脱口而出&#xff1a;"是的&#xff01;"但其实这个答案并不完全正确。今天我们就来聊聊Redis的线程模型&#xff0c;把这个问题彻底搞清楚。 先说结论 Redis的线程…

Hologres实战:路径分析函数

前言 Hologres提供了一套高效的路径分析函数&#xff0c;包括路径明细计算和结果解析功能&#xff0c;能够帮助用户深入理解用户行为路径&#xff0c;并通过桑基图实现数据可视化。 一、核心功能 路径明细计算&#xff1a;精确记录用户在产品或功能中的完整访问路径结果解析…

产品开发实践(常见的软硬结合方式)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】前面说过&#xff0c;传统的纯软件开发&#xff0c;在国内的大背景下面是很难存活的。但是如果是把软件&#xff0c;构建在硬件基础之上&#xff0c…

Linux | i.MX6ULL网络通信-套字节 UDP(第十八章)

01 Linux | i.MX6ULL网络通信-套字节 TCP(第十七章) 02 iTOP-IMX6ULL 实现基于 UDP 的 socket 编程。

学习嵌入式第三十天

文章目录进程和线程&#xff08;续&#xff09;线程1.线程传参2.线程属性3.线程间通信1.概念2.方式3.互斥锁4.死锁5.信号量习题 进程和线程&#xff08;续&#xff09; 线程 1.线程传参使用第四个参数实现对线程内部的传参 代码实现&#xff1a; #include <stdio.h> #inc…

GaussDB 数据库架构师修炼(十三)安全管理(3)-行级访问控制

1 背景行级访问控制特性将数据库的访问控制精确到数据表行级别 &#xff0c;只允许用户查看 、更新或删除特定的行数据。2 实例场景实例以医生只能看到治疗的病人&#xff0c;不能看其它医生的病人为例&#xff1a;1)医院病人的信息表pat_info&#xff1a;csdn> set search_…

Wi-Fi 与蜂窝网络(手机网络)的核心区别,以及 Wi-Fi 技术未来的发展方向

在日常生活中&#xff0c;我们既离不开家里的 Wi-Fi&#xff0c;也离不开手机的 4G/5G 网络。它们都能把我们连接到互联网&#xff0c;但底层的工作方式却大不相同。一、设计初衷的不同Wi-Fi诞生于 1997 年的 IEEE 802.11 标准&#xff0c;定位是局域网无线替代。它的目标是让电…

C++编程实战:高效解决算法与数据结构问题

个人主页 &#xff1a; zxctscl 专栏 【C】、 【C语言】、 【Linux】、 【数据结构】、 【算法】 如有转载请先通知 题目1. 数字统计2. 两个数组的交集3. 牛牛的快递4. 点击消除5. 最小花费爬楼梯6. 简写单词1. 数字统计 BC153 数字统计 #include <iostream> using na…

《零基础入门AI:深度学习中的视觉处理(卷积神经网络(CNN)进阶)》

一、卷积知识扩展 1. 二维卷积 单通道版本 对于单通道输入图像 III (尺寸 HWH \times WHW) 和卷积核 KKK (尺寸 FFF \times FFF)&#xff0c;输出特征图 OOO 的计算公式为&#xff1a; O(i,j)∑m0F−1∑n0F−1I(im,jn)⋅K(m,n)O(i,j) \sum_{m0}^{F-1} \sum_{n0}^{F-1} I(im, j…

pyecharts可视化图表-pie:从入门到精通(进阶篇)

欢迎来到pyecharts饼图系列教程的进阶篇&#xff01;在上一篇基础教程中&#xff0c;我们学习了饼图的基本概念和简单实现。在本文中&#xff0c;我们将深入探索pyecharts中饼图的六种高级用法和自定义选项&#xff0c;包括环形饼图、富文本标签饼图、滚动图例饼图、环形图、嵌…

【JAVA 核心编程】面向对象高级:类变量与方法 抽象类与接口

一、类变量与类方法&#xff08;静态变量&#xff09; 1&#xff09;类变量 class Child{private String name;//定义一个变量count&#xff0c;是一个类变量&#xff08;静态变量&#xff09;static静态//该变量最大的特点就是会被Child 类的所有对象访问public static int co…

【Java基础面试题】数据类型

Java面试高频总结&#xff1a;基本数据类型深度解析 &#x1f4ca; 八种基本数据类型详解数据类型关键字字节数位数默认值取值范围核心特性字节型byte180-128 ~ 127最小整数类型短整型short2160-32,768 ~ 32,767较少使用整型int4320-2 ~ 2-1 (约21亿)最常用整数类型长整型long8…

攻防世界—unseping(反序列化)

一.审题<?php highlight_file(__FILE__);class ease{private $method;private $args;function __construct($method, $args) {$this->method $method;$this->args $args;}function __destruct(){if (in_array($this->method, array("ping"))) {call_u…

AI热点周报(8.10~8.16):AI界“冰火两重天“,GPT-5陷入热议,DeepSeek R2模型训练受阻?

名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录3分钟速览版&#xff1a;一张表看懂本周AI大事一、GPT-5&#xff1a;期待越高&#x…

Python_vue3_django旅拍在线婚纱摄影网站的设计与实现016023190_源码LW_讲解安装

目录前言-本系统介绍已开发项目效果实现截图开发技术详细介绍论文设计框架系统测试核心代码参考示例总结源码获取详细视频演示或者查看其他版本&#xff1a;文章底部获取博主联系方式&#xff01;前言-本系统介绍 利用Python语言、MySQL数据库&#xff0c;Django框架&#xff0…

Python爬虫-爬取政务网站的文档正文内容和附件数据

前言 本文是该专栏的第67篇,后面会持续分享python爬虫干货知识,记得关注。 本文,笔者以某政务网站为例子。基于Python爬虫采集某政务网站的文档正文内容和其关联的附件数据。 具体的实现思路以及完整实现代码逻辑,笔者将在正文进行详细介绍。废话不多说,跟着笔者直接往下…

Python:如何在Pycharm中显示geemap地图?

01 说明 或许在旧版本的python和jupyter中并不能成功. 作为参考&#xff0c;这里给出实验成功的版本&#xff1a;名称版本通道geemap0.36.1conda-forgejupyter1.1.1conda-forgepycharm2024.1.4 (Professional Edition)nullpython3.11.13conda-forge此外&#xff0c;由于显示底图…

力扣3:无重复字符的最长子串

力扣3:无重复字符的最长子串题目思路代码题目 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长 子串 的长度。 思路 这道题的思路其实是很简单的&#xff0c;最后我们需要得到子串的长度所以我们可以定义两个变量即子串的左边界和右边界这样有了左右边界就…