【LeetCode Hot100 动态规划】

动态规划

    • 动态规划五部曲
    • 简单动态规划问题
      • 爬楼梯
      • 打家劫舍
    • 01背包类问题
      • 01背包基础
      • 二维动态数组
      • 一维动态数组
      • 分割等和子集
    • 完全背包类问题
      • 完全背包基础
      • 零钱兑换
      • 完全平方数
      • 零钱兑换II
      • 组合总和IV
      • 单词拆分
    • 子序列问题
      • 最长递增子序列
      • 乘积最大子数组

动态规划五部曲

确定dp数组(dp table)以及下标的含义
确定递推公式
dp数组如何初始化
确定遍历顺序
举例推导dp数组

简单动态规划问题

爬楼梯

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

dp数组含义
dp[i]:爬 i 层楼梯有 dp[i] 种方法

递推公式
爬到第 i 层,由于每次只能爬 1 或 2 个台阶,那么上一步可能在第 i-1 层或者第 i - 2 层。爬到第 i-1 层有 dp[i-1] 种方法,爬到第 i-2 层有 dp[i-2] 种方法,所以得到递推公式:
dp[i] = dp[i-1] + dp[i-2];

初始化
dp[0] = 1;
dp[1] = 1;

遍历顺序
因为得到 dp[i] 需要知道 dp[i-1] 和 dp[i-2],所以是正序遍历。

代码

class Solution {public int climbStairs(int n) {int[] dp = new int[n+1];dp[0] = 1;dp[1] = 1;for(int i = 2; i <= n; i++) {dp[i] = dp[i-1] + dp[i-2];}return dp[n];}
}

打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

dp数组含义
dp[i] 考虑下标 i (包含), 偷的最大的金币数量。

递推公式
在 i 的位置,考虑:
偷 i :dp[i-2] + nums[i] // i - 1 不能偷,所以是 dp[i-2] 然后加上第 i 个
不偷 i :dp[i-1] // 考虑i-1,但是不代表 i-1 一定被偷
综上,得到递推公式:
dp[i] = max(dp[i-2] + nums[i], dp[i-1])

初始化
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1])

遍历顺序
正序

代码

class Solution {public int rob(int[] nums) {int[] dp = new int[nums.length];dp[0] = nums[0];if (nums.length == 1) {return dp[0];}dp[1] = Math.max(nums[0], nums[1]);for (int i = 2; i <= nums.length - 1; i++) {dp[i] = Math.max(dp[i-2] + nums[i], dp[i-1]);}return dp[nums.length - 1];}
}

01背包类问题

01背包基础

有 n 件物品和一个最多能背重量为 w 的背包。第 i 件物品的重量是 weight[i],得到的价值是 value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

二维动态数组

dp数组含义
i 来表示物品、j表示背包容量。
dp[i][j] 表示从下标为 [0-i] 的物品里任意取,放进容量为 j 的背包,价值总和最大是多少。

递推公式
不放物品 i:背包容量为 j,里面不放物品 i 的最大价值是 dp[i - 1][j]。
放物品 i:背包空出物品 i 的容量后,背包容量为 j - weight[i],dp[i - 1][j - weight[i]] 为背包容量为 j - weight[i] 且不放物品 i 的最大价值,那么 dp[i - 1][j - weight[i]] + value[i] (物品i的价值),就是背包放物品 i 得到的最大价值

递归公式: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);

遍历顺序
先遍历物品或者先遍历背包都可以。

一维动态数组

dp数组含义
dp[j] 表示: 容量为 j 的背包,所背的物品价值最大可以为 dp[j]。

递推公式
dp[j] = max(dp[j], dp[j-weight[i]] + value[i])

初始化
全都初始化为0

遍历顺序
遍历顺序:先遍历物品,后遍历背包,遍历背包要倒序遍历(因为是01背包问题,每一个物品只能选择一次)

分割等和子集

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。
示例 1:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

dp数组含义
在本题中,所有元素和为sum,那么能在数组中找到元素和为 sum/2 的数字,就返回true。
将该问题抽象为01背包,每一个数字的重量和价值都是数字的数值。sum/2 就是背包的容量和价值,dp[sum/2] = sum/2 背包就装满了,即找到了元素和为 sum/2 的数字。
01背包中,dp[j] 表示: 容量(所能装的重量)为 j 的背包,所背的物品价值最大可以为 dp[j]。
如果背包所载重量为target, dp[target]就是装满 背包之后的总价值,因为 本题中每一个元素的数值既是重量,也是价值,所以,当 dp[target] = target 的时候,背包就装满了。

递推公式
这里是使用一维dp动态数组
dp[j] = max(dp[j], dp[j-nums[i]] + nums[i])

初始化
初始化为0

遍历顺序
遍历顺序:先遍历物品,后遍历背包,遍历背包要倒序遍历(因为是01背包问题,每一个物品只能选择一次)

代码

class Solution {// 所有元素和为sum,那么能在数组中找到元素和为 sum/2 的数字,就返回truepublic boolean canPartition(int[] nums) {int sum = 0;for(int i = 0; i < nums.length; i++) {sum += nums[i];}//总和为奇数,不能平分if(sum % 2 != 0) return false;int[] dp = new int[sum / 2 + 1];for(int i = 0; i < nums.length; i++) {for(int j = sum / 2; j >= nums[i]; j--) {dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);}}if (dp[sum / 2] == sum / 2) {return true;}return false;}
}

完全背包类问题

完全背包基础

有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。

完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。

零钱兑换

给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。
计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1

dp数组含义
把该问题抽象为完全背包问题
dp[j] 装满容量为j的背包,最少物品数为 dp[j]
即凑够总金额为 j 时,最少的硬币个数为 dp[j]

递推公式
选第 i 个时:dp[j-coins[i]] + 1
不选第 i 个时:dp[j]
dp[j] = min(dp[j-coins[i]] + 1, dp[j])

初始化
dp[0] = 0,
由于状态方程取min,所以非零下标初始化为INT_Max

遍历顺序
因为是求最小硬币个数,所以先遍历物品或者背包都可以;
因为是完全背包,物品可以选无数次,所以 j 是正序遍历

代码

class Solution {public int coinChange(int[] coins, int amount) {int[] dp = new int[amount + 1];Arrays.fill(dp, Integer.MAX_VALUE); dp[0] = 0;  // 初始化0for (int i = 0; i < coins.length; i++) {for (int j = coins[i]; j <= amount; j++) {//只有dp[j-coins[i]]不是初始最大值时,该位才有选择的必要if (dp[j - coins[i]] != Integer.MAX_VALUE) {//选择硬币数目最小的情况dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1);}}}return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount];}
}

完全平方数

给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

dp数组含义
把该问题抽象为完全背包问题
dp[j] 装满容量为j的背包,最少物品数为 dp[j]
即凑够整数 n 时,最少需要的完全平方数个数为 dp[j]

递推公式
选第 i 个时:dp[j - i * i] + 1
不选第 i 个时:dp[j]
dp[j] = min(dp[j-coins[i]] + 1, dp[j])

初始化
dp[0] = 0,
由于状态方程取min,所以非零下标初始化为INT_Max

遍历顺序
因为是求最少完全平方数的个数,所以先遍历物品或者背包都可以;
因为是完全背包,物品可以选无数次,所以 j 是正序遍历

代码

class Solution {public int numSquares(int n) {int[] dp = new int[n + 1];Arrays.fill(dp, Integer.MAX_VALUE);dp[0] = 0;for (int i = 1; i*i <= n; i++) {for (int j = i*i; j <= n; j++) {dp[j] = Math.min(dp[j], dp[j - i*i] + 1);}}return dp[n];}
}

零钱兑换II

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。
假设每一种面额的硬币有无限个。
题目数据保证结果符合 32 位带符号整数。

dp数组含义
把该问题抽象为完全背包问题
dp[j] 装满容量为j的背包,有 dp[j] 种装法
即凑够总金额,共有 dp[j] 种凑法。

递推公式
dp[j] += dp[ j - coins[i] ]

初始化
dp[0] = 1

遍历顺序
求的是组合,因此要先遍历物品后遍历背包
因为是完全背包问题,所以背包是正序遍历。

代码

class Solution {public int change(int amount, int[] coins) {int[] dp = new int[amount+1];dp[0] = 1;for (int i = 0; i <= coins.length - 1; i++) {for (int j = coins[i]; j <= amount; j++) {dp[j] += dp[j- coins[i]];}}return dp[amount];}
}

组合总和IV

给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。
题目数据保证答案符合 32 位整数范围。
示例 1:
输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。

dp数组含义
把该问题抽象为完全背包问题
dp[j] 装满容量为j的背包,有 dp[j] 种装法
即凑够目标整数 target,共有 dp[j] 种凑法。

递推公式
dp[j] += dp[ j - coins[i] ]

初始化
dp[0] = 1

遍历顺序
求的是排列(不同排列顺序视为不同的组合),因此要先遍历背包后遍历物品
因为是完全背包问题,所以背包是正序遍历。

代码

class Solution {public int combinationSum4(int[] nums, int target) {int[] dp = new int[target + 1];dp[0] = 1;for (int j = 0; j <= target; j++) {for (int i = 0; i < nums.length; i++) {if (j >= nums[i]) {// 排列问题的话需要判断容量dp[j] += dp[j - nums[i]];}}}return dp[target];}
}

单词拆分

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例 1:
输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以由 “leet” 和 “code” 拼接成。

dp数组含义
单词就是物品,字符串s就是背包,单词能否组成字符串s,就是问物品能不能把背包装满。
拆分时可以重复使用字典中的单词,说明就是一个完全背包!

dp[i] : 字符串长度为 i 的话,dp[i] 为true,表示可以拆分为一个或多个在字典中出现的单词。

递推公式
如果确定 dp[j] 是true,且 [j, i] 这个区间的子串出现在字典里,那么dp[i]一定是true。(j < i )。
所以递推公式是 if ( [j, i] 这个区间的子串出现在字典里 && dp[j] 是true ) 那么 dp[i] = true。

初始化
dp[0] = true ;
非零下标初始化为 false;

遍历顺序
本题需要强调物品之间的顺序:
拿 s = “applepenapple”, wordDict = [“apple”, “pen”] 举例。
“apple”, “pen” 是物品,那么我们要求 物品的组合一定是 “apple” + “pen” + “apple” 才能组成 “applepenapple”。
因此是求排列,那么就要先遍历背包再遍历物品。

代码

class Solution {public boolean wordBreak(String s, List<String> wordDict) {HashSet<String> set = new HashSet<>(wordDict);boolean[] bp = new boolean[s.length() + 1];bp[0] = true;// 先遍历背包容量for(int i = 1; i <= s.length(); i++) {// 后遍历物品;!bp[i] 作用是剪枝for (int j = 0; j < i && !bp[i]; j++) {if (set.contains(s.substring(j, i)) && bp[j]) {bp[i] = true;}}}return bp[s.length()];}
}

子序列问题

最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

dp数组含义
dp[i] 表示 i 之前包括i 的以 nums[i] 结尾的最长递增子序列的长度

递推公式
位置 i 的最长升序子序列等于 j 从 0 到 i-1 各个位置的最长升序子序列 + 1 的最大值。
所以:if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);
注意这里不是要dp[i] 与 dp[j] + 1进行比较,而是我们要取dp[j] + 1的最大值。

初始化
全部初始化为 1

代码

class Solution {public int lengthOfLIS(int[] nums) {int[] dp = new int[nums.length];Arrays.fill(dp, 1);int result = 1;for (int i = 1; i < nums.length; i++) {for (int j = 0; j < i; j++) {// 内循环寻找 从 0 到 i - 1 各个位置的最长升序子序列if (nums[i] > nums[j]) {dp[i] = Math.max(dp[i], dp[j] + 1);}}result = Math.max(result, dp[i]);}return result;}
}

乘积最大子数组

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

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

相关文章

python康威生命游戏的图形化界面实现

康威生命游戏&#xff08;Conway’s Game of Life&#xff09;是由英国数学家约翰何顿康威&#xff08;John Horton Conway&#xff09;在1970年发明的一款零玩家的细胞自动机模拟游戏。尽管它的名字中有“游戏”&#xff0c;但实际上它并不需要玩家参与操作&#xff0c;而是通…

【数据结构】链表应用-链表重新排序

重新排序 反转链表预期实现思路解题过程code力扣代码核心代码完整代码 总结 删除链表中间节点代码解惑 链表重新排序题目描述解题思路解题过程复杂度代码力扣代码完整代码 反转链表 预期实现 思路 你选用何种方法解题&#xff1f; 我选用了迭代法来反转链表。这是一种经典且高…

使用mockttp库模拟HTTP服务器和客户端进行单元测试

简介 mockttp 是一个用于在 Node.js 中模拟 HTTP 服务器和客户端的库。它可以帮助我们进行单元测试和集成测试&#xff0c;而不需要实际发送 HTTP 请求。 安装 npm install mockttp types/mockttp模拟http服务测试 首先导入并创建一个本地服务器实例 import { getLocal } …

pytest-xdist 进行多进程并发测试!

在软件开发过程中&#xff0c;测试是确保代码质量和可靠性的关键步骤。随着项目规模的扩大和复杂性的增加&#xff0c;测试用例的执行效率变得尤为重要。为了加速测试过程&#xff0c;特别是对于一些可以并行执行的测试用 例&#xff0c;pytest-xdist 提供了一种强大的工具&…

mysql8安装时提示-缺少Microsoft Visual C++ 2019 x64 redistributable

MySQL8.0安装包mysql-8.0.1-winx64进行安装&#xff0c;提示&#xff1a;This application requires Visual Studio 2019 x64Redistributable, Please install the Redistributable then runthis installer again。出现这个错误是因为我们电脑缺少Microsoft Visual C 这个程序&…

基于HTML生成网页有什么优势

在互联网时代&#xff0c;网页是人们获取信息、交流互动的重要窗口&#xff0c;而基于HTML生成网页&#xff0c;是搭建网络大厦的关键。HTML语法简洁直观&#xff0c;标签和属性语义明确&#xff0c;新手也能迅速上手&#xff0c;创建包含基础元素的网页&#xff0c;极大降低了…

【MySQL】深度理解事务的隔离性:全面讲解事务的四种隔离级别

**前言&#xff1a;**上节内容我们主要说了如果没有设置保存点&#xff0c; 也可以回滚&#xff0c;但是只能回滚到事务的开始。直接使用rollback的前提是事务还没有提交。并且如果一个事务被提交了&#xff0c;就不可以回退。同时我们也可以使用savepoint设置回滚点。 可以自己…

项目实战 —— HTTP服务器设计与实现

目录 一&#xff0c;项目介绍 二&#xff0c;背景知识补充 2.1 http特点 2.2 URI&#xff0c;URL&#xff0c;URN 2.3 http请求方法 三&#xff0c;前置功能实现 3.1 日志编写 3.2 封装相关套接字 3.3 http请求结构设计 3.4 http响应结构设计 3.5 http服务器主体逻辑…

Verilog 语法篇 硬件描述语言

Verilog 是一种硬件描述语言&#xff0c;用于设计、模拟和综合数字电路和系统。它主要用于描述 ASIC&#xff08;专用集成电路&#xff09;或 FPGA&#xff08;现场可编程门阵列&#xff09;等硬件设备的结构和行为。 定义与用途&#xff1a; Verilog 是一种硬件描述语言&#…

GitHub Copilot:智能助手觉醒

GitHub Copilot: The agent awakens - The GitHub Blog github copilot 官方文档刚刚宣布支持 agent 模式&#xff01; 这一模式和之前的 chat 方式不同&#xff0c;类似于 cursor 可以根据需求直接运行、调试和修改代码 这一模式在 preview 版本可以使用&#xff0c;并且需…

网络安全威胁框架与入侵分析模型概述

引言 “网络安全攻防的本质是人与人之间的对抗&#xff0c;每一次入侵背后都有一个实体&#xff08;个人或组织&#xff09;”。这一经典观点概括了网络攻防的深层本质。无论是APT&#xff08;高级持续性威胁&#xff09;攻击、零日漏洞利用&#xff0c;还是简单的钓鱼攻击&am…

SystemUI中NavigationBar分析

需求 SystemUI是一个与系统组件显示紧密相关的应用&#xff0c;包含快捷中心、消息通知、状态栏、导航栏、任务中心等诸多模块&#xff0c;本文介绍NavigationBar模块。SystemUI源码位于/frameworks/base/packages/SystemUI&#xff0c;Android13平台。NavigationBar显示如下&…

【人工智能】Python中的序列到序列(Seq2Seq)模型:实现机器翻译

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 序列到序列(Seq2Seq)模型是自然语言处理(NLP)中一项核心技术,广泛应用于机器翻译、语音识别、文本摘要等任务。本文深入探讨Seq2Seq模…

深入浅出谈VR(虚拟现实、VR镜头)

1、VR是什么鬼&#xff1f; 近两年VR这次词火遍网上网下&#xff0c;到底什么是VR&#xff1f;VR是“Virtual Reality”&#xff0c;中文名字是虚拟现实&#xff0c;是指采用计算机技术为核心的现代高科技手段生成一种虚拟环境&#xff0c;用户借助特殊的输入/输出设备&#x…

postman免登录版本,实测可用(解决一直卡在登录界面无法进入的问题)

一、背景 2025今年开工后&#xff0c;打开postman&#xff0c;一直提示需要登录&#xff0c;但是一直卡在登录界面&#xff0c;好几个人的postman都是这样的情况&#xff0c;不知道是什么原因。 折腾几小时无果&#xff0c;网上下载了各种版本都试了&#xff0c;最新的版本也…

猫眼Java开发面试题及参考答案(上)

详细介绍项目,像项目中如何用 Redis,用到 Redis 哪些数据类型,项目中遇到哪些问题,怎么解决的 在我参与的一个电商项目中,Redis 发挥了至关重要的作用。这个电商项目主要是为用户提供商品浏览、购物车管理、订单处理等一系列功能。 在项目中使用 Redis 主要是为了提升系统…

.net知识点4

1.struct与class有何异同 异&#xff1a; struct无法实现继承 class可以继承 同 都可以实例化 都可以实现接口 都可以定义字段&#xff0c;属性&#xff0c;方法体 2.Class有了自定义的&#xff0c;带参的构造函数后&#xff0c;有什么特点 这个类在实例化的时候&…

设计模式 ->模板方法模式(Template Method Pattern)

模板方法模式 模板方法模式是一种行为设计模式&#xff0c;它在一个方法中定义一个操作的算法骨架&#xff0c;而将一些步骤延迟到子类中实现。它允许子类在不改变算法结构的情况下重新定义算法中的某些步骤 特点 算法骨架&#xff1a; 在基类中定义算法的框架延迟实现&…

Unity中Spine骨骼动画完全指南:从API详解到避坑实战

Unity中Spine骨骼动画完全指南&#xff1a;从API详解到避坑实战 一、为什么要选择Spine&#xff1f; Spine作为专业的2D骨骼动画工具&#xff0c;相比传统帧动画可节省90%资源量。在Unity中的典型应用场景包括&#xff1a; 角色换装系统&#xff08;通过插槽替换部件&#xf…

数据库开发常识(10.6)——SQL性能判断标准及索引误区(1)

10.6. 数据库开发常识 作为一名专业数据库开发人员,不但需要掌握数据库开发相关的语法和功能实现,还要掌握专业数据库开发的常识。这样,才能在保量完成工作任务的同时,也保质的完成工作任务,避免了为应用的日后维护埋下性能和稳定性方面的隐患。可遗憾的是,现实中,很大…