从0开始掌握动态规划

动态规划的核心思想 -- 以空间换时间

复杂点说通过分解问题为子问题并存储子问题解来优化复杂计算的算法策略。

简单看个问题。

一,初始:求最长连续递增子序列

nums = [10,9,2,5,3,7,101,18]

求上面数组中的最长连续递增子序列,输出其长度

暴力解法中我们需要以每个数为开头去遍历数组,得到最长的子数组。

会产生如下遍历

3,7,101

7,101

可以看到,有重复的计算。我们先用java写一下暴力解法。

暴力解法
    public int lengthOfLIS(int[] nums) {if (nums == null || nums.length == 0) {return 0;}int res = 0;for (int i = 0; i < nums.length; i++) {int temp = 1; // 当前子序列的长度,初始为1(单个元素)for (int j = i + 1; j < nums.length; j++) {if (nums[j] > nums[j - 1]) {temp++;} else {break;}}res = Math.max(res, temp); // 更新最长子序列的长度}return res;}

双重for循环,时间复杂度为 (O(n^2)),空间复杂度为 (O(1))。

动态规划

初始化dp的int数组。值为到当前位置的最大连续数。这样整个遍历完dp数组的值应该是

[1,1,1,2,1,2,3,1]。 可以看到最大长度为3。

    /*** 方法二: 动态规划求解连续递增子序列* @param nums* @return*/public int lengthOfLIS2(int[] nums) {if (nums == null || nums.length == 0) {return 0;}int n = nums.length;int[] dp = new int[n];dp[0] = 1; // 单个元素本身就是一个长度为 1 的连续递增子序列int res = 1; // 初始化结果为 1for (int i = 1; i < n; i++) {if (nums[i] > nums[i - 1]) {dp[i] = dp[i - 1] + 1;} else {dp[i] = 1; // 当前元素本身可以作为一个新的连续递增子序列的起点}res = Math.max(res, dp[i]); // 更新最长子序列的长度}return res;}

时间复杂度:(O(n))
空间复杂度:(O(n))

二,升级:求最长递增子序列

nums = [10,9,2,5,3,7,101,18]

求上面数组中的最长递增子序列,输出其长度

将上面一道题进行稍微变化一下,我们可以看出,现在的结果应该是2,3,7,101。输出结果应该是4。

如果不用动态规划,这道题有点难解。需要用到回溯+记忆,单纯的暴力解法并不适用。

比如我再加一个测试用例

nums =[0,1,0,3,2,3]

 回溯法
    /*** 方法一: 暴力解法* @param nums* @return*/public int lengthOfLIS(int[] nums) {if (nums == null || nums.length == 0) return 0;int maxLen = 0;for (int i = 0; i < nums.length; i++) {maxLen = Math.max(maxLen, backtrack(nums, i, new ArrayList<>()));}return maxLen;}// 回溯法生成所有递增子序列private int backtrack(int[] nums, int index, List<Integer> current) {if (index >= nums.length) return current.size();int maxLen = current.size();  // 不选当前元素的默认长度// 选择当前元素的情况if (current.isEmpty() || nums[index] > current.get(current.size() - 1)) {current.add(nums[index]);int lenWith = backtrack(nums, index + 1, current);current.remove(current.size() - 1);maxLen = Math.max(maxLen, lenWith);}// 不选当前元素的情况int lenWithout = backtrack(nums, index + 1, current);return Math.max(maxLen, lenWithout);}

时间复杂度: (O(2^n))

空间复杂度:O(n)

该方法容易超过时间限制

动态规划

这个就需要在上个动态规划样例中稍微改动一下。

我们先模拟一下对照样例生成的dp数组应该的样子。

int[] nums = {10, 9, 2,  5, 3, 7, 101, 18};
int[] dp =   {1,  1, 1,  2, 2, 3,  4,  4};

先举个简单的方便观察的例子

int[] nums = {1, 2, 3, 4, 2, 6, 7};
int[] dp =   {1, 2, 3, 4, 1, 5, 6};

即便当数组为递减数组时,长度也会输出1

  1. 所以我们还是需要先将dp数组全部初始化1
  2. 第一次遍历,2 > 1的,所以dp[1] = dp[0] + 1;
  3. 第二次遍历,3 > 1的,所以dp[2] = dp[0] + 1; 然后 3 > 2的,所以 dp[2] = dp[1] + 1;
  4. ……
  5. 第五次遍历,6 > 4的,所以dp[5] = dp[3] + 1;但是6 > 2,不能得出dp[5] = dp[4] + 1,需要dp[4] + 1与当前的dp[5]的值进行比较。取最大的一个。
  6. ……

所以综上我们可以写出代码

    /*** 方法二: 动态规划求解递增子序列* @param nums* @return*/public int lengthOfLIS2(int[] nums) {if (nums == null || nums.length == 0) {return 0;}int n = nums.length;int[] dp = new int[n];Arrays.fill(dp, 1);int res = 1; // 初始化结果为 1for (int i = 1; i < n; i++) {for (int j = 0; j < i; j++) {if (nums[j] < nums[i]) {dp[i] = Math.max(dp[i], dp[j] + 1);}res = Math.max(res, dp[i]);}}return res;}

 时间复杂度为 ​O(n²),空间复杂度为 ​O(n)。

三,再升级:删除最少元素,使剩余元素先递增后递减

动态规划

还是以刚刚的例子为例

nums = [10,9,2,5,3,7,101,18]

删除最少元素,使剩余元素先递增后递减,输出最少需要删除多少元素。

 通过上面两个例子,我们可以看出一个dp数组只能保存一个状态。

对于这道题,我们可以考虑使用两个dp数组来完成。

nums = [10,9,2,5,3,7,101,18]
int[] dp1 =   {1,  1, 1,  2, 2, 3,  4,  4}; //递增dp
int[] dp2 =   {4,  3, 1,  2, 1, 1,  2,  1}; //递减dp

 可以看出在dp1[6] + dp2[6] = 6的时候是最大的,所以最大长度应该是dp1[6] + dp2[6] - 1 = 5;

因为6这个节点重复计算了,所以需要减1

那么就能得出答案需要删除:nums.length() - 5 = 3;

最少需要删除3个元素。

    /*** 方法二: 动态规划求解删除最少元素,使剩余元素先递增后递减* @param nums* @return*/public int lengthOfLIS3(int[] nums) {if (nums == null || nums.length == 0) {return 0;}int n = nums.length;int[] dp1 = new int[n];int[] dp2 = new int[n];Arrays.fill(dp1, 1);Arrays.fill(dp2, 1);// 递增子序列for (int i = 1; i < n; i++) {for (int j = 0; j < i; j++) {if (nums[j] < nums[i]) {dp1[i] = Math.max(dp1[i], dp1[j] + 1);}}}// 递减子序列for (int i = n - 2; i >= 0; i--) {for (int j = n - 1; j > i; j--) {if (nums[j] < nums[i]) {dp2[i] = Math.max(dp2[i], dp2[j] + 1);}}}// 合并int max = 0;for (int i = 0; i < n; i++) {if (dp1[i] + dp2[i] > max) {max = dp1[i] + dp2[i];}}return n - max - 1;}

 四,看一道力扣原题

322. 零钱兑换

 这道题可以换个角度看看,我们要求amount的最小硬币个数。可以构建dp[amount + 1]数组,

求从1 - amount间的所有最小个数。

换个简单的实例

输入:coins = [1, 2], amount = 5

以外层coins为循环,内层循环amount得到最小值

如上面例子。

初始化dp数组,因为要求最小,所以初始值应该为最大,且dp[0] = 0;

第一次外层循环1得到dp数组[0,1,2,3,4,5]

第二次外层循环2得到dp数组[0,1,1,2,2,3]

构建dp公式:

dp[i] = Math.min(dp[i], dp[i - coin] + 1);

 得到解法

    public int coinChange(int[] coins, int amount) {int[] dp = new int[amount + 1];Arrays.fill(dp, amount + 1); // 初始化为不可达标记dp[0] = 0; // 基础情况// 外层循环遍历硬币for (int coin : coins) {// 内层循环遍历金额(完全背包正序遍历)for (int i = coin; i <= amount; i++) {dp[i] = Math.min(dp[i], dp[i - coin] + 1);}}return dp[amount] > amount ? -1 : dp[amount];}

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

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

相关文章

Python Requests 库:从安装到精通

摘要 本文详细介绍 Python Requests 库的安装与使用&#xff0c;通过常见示例让你轻松掌握。 一、引言 在当今的互联网时代&#xff0c;与各种 Web 服务进行交互是非常常见的需求。Python 作为一门功能强大且易于学习的编程语言&#xff0c;提供了许多用于网络请求的库&…

Manus技术架构、实现内幕及分布式智能体项目实战

Manus技术架构、实现内幕及分布式智能体项目实战 模块一&#xff1a; 剖析Manus分布式多智能体全生命周期、九大核心模块及MCP协议&#xff0c;构建低幻觉、高效且具备动态失败处理能力的Manus系统。 模块二&#xff1a; 解析Manus大模型Agent操作电脑的原理与关键API&#xf…

C算术运算符 printf输出格式 字符指针打印输出 使用scanf函数进行输入

一 算术运算符 加, 一元取正 - 减, 一元取负 * 乘 / 除 % 求余 -- 自减1 自加1 逻辑运算符 && 逻辑与 || 逻辑或 ! 逻辑非 关系运算符 > 大于 > 大于等于 < 小于 < 小于等于 等于 ! 不等于 位运算符号 & 按位与 | 按位或 ^ 按位异或…

STM32中Hz和时间的转换

目录 一、常见的频率单位及其转换 二、计算公式 三、STM32中定时器的应用 四、例子 一、常见的频率单位及其转换 赫兹&#xff08;Hz&#xff09;是频率的国际单位&#xff0c;表示每秒钟周期性事件发生的次数。 1 kHz&#xff08;千赫兹&#xff09; 1,000 Hz1 MHz&#…

《分布式软总线:不同频段Wi-Fi环境下设备发现兼容性难题》

分布式软总线技术作为实现设备互联互通的关键&#xff0c;正逐渐成为构建万物互联世界的基石。然而&#xff0c;当分布式软总线面临不同频段Wi-Fi环境时&#xff0c;设备发现的兼容性问题成为了阻碍其广泛应用的一大挑战。这一问题不仅影响着用户体验&#xff0c;也制约着分布式…

MCP(Model Context Protocol 模型上下文协议)科普

MCP&#xff08;Model Context Protocol&#xff0c;模型上下文协议&#xff09;是由人工智能公司 Anthropic 于 2024年11月 推出的开放标准协议&#xff0c;旨在为大型语言模型&#xff08;LLM&#xff09;与外部数据源、工具及服务提供标准化连接&#xff0c;从而提升AI在实际…

【mongodb】数据库操作

目录 1. 查看所有数据库2. 切换到指定数据库&#xff08;若数据库不存在&#xff0c;则创建&#xff09;3. 查看当前使用的数据库4. 删除当前数据库5.默认数据库 1. 查看所有数据库 1.show dbs2.show databases 2. 切换到指定数据库&#xff08;若数据库不存在&#xff0c;则…

ICPR-2025 | 让机器人在未知环境中 “听懂” 指令精准导航!VLTNet:基于视觉语言推理的零样本目标导航

作者&#xff1a;Congcong Wen, Yisiyuan Huang, Hao Huang ,Yanjia Huang, Shuaihang Yuan, YuHao, HuiLin and Yi Fang 单位&#xff1a;纽约大学阿布扎比分校具身人工智能与机器人实验室&#xff0c;纽约大学阿布扎比分校人工智能与机器人中心&#xff0c;纽约大学坦登工程…

基于DeepSeek的考研暑假日志分析

注&#xff1a;我去年考研时写了日志&#xff0c;大致记录了我每天的主要活动。由于过于琐碎&#xff0c;一直没有翻看。突发奇想&#xff0c;现在利用deepseek总结其中规律。 从你的日志中可以总结出以下规律和活动兴衰起落&#xff1a; ​​一、学习活动规律与演变​​ ​​…

【刷题Day20】TCP和UDP

TCP 和 UDP 有什么区别&#xff1f; TCP提供了可靠、面向连接的传输&#xff0c;适用于需要数据完整性和顺序的场景。 UDP提供了更轻量、面向报文的传输&#xff0c;适用于实时性要求高的场景。 特性TCPUDP连接方式面向连接无连接可靠性提供可靠性&#xff0c;保证数据按顺序…

REST 架构详解:从概念到应用的全面剖析

REST&#xff08;Representational State Transfer&#xff09;即表述性状态转移&#xff0c;是一种用于构建网络应用程序的架构风格和设计理念&#xff0c;由计算机科学家罗伊・菲尔丁&#xff08;Roy Fielding&#xff09;在 2000 年提出。以下是关于它的详细介绍&#xff1a…

蓝桥杯之递归二

1.数的划分 题目描述 将整数 nn 分成 kk 份&#xff0c;且每份不能为空&#xff0c;任意两份不能相同(不考虑顺序)。 例如&#xff1a;n7&#xff0c;k3n7&#xff0c;k3&#xff0c;下面三种分法被认为是相同的。 1&#xff0c;1&#xff0c;5;1&#xff0c;5&#xff0c;…

LeetCode(Hot.2)—— 49.字符异位词分组题解

Problem: 49. 字母异位词分组 字母异位词的定义是&#xff1a;两个单词的字母组成一样&#xff0c;但顺序可以不同&#xff0c;比如 eat、tea 和 ate 就是一个组的。 思路 将每个字符串按字母排序&#xff0c;把排序后的字符串作为 key&#xff0c;相同 key 的放在一个 list 中…

为什么信号完整性对于高速连接器设计至关重要?

外部连接器通过在各种电子元件和系统之间可靠地传输数据而不损失保真度来保持信号完整性。在本文中&#xff0c;我们将讨论信号完整性的重要性&#xff0c;回顾高速部署挑战&#xff0c;并重点介绍各种连接器设计策略&#xff0c;以防止失真和降级。 了解连接器信号完整性挑战…

得物官网sign签名逆向分析

打开得物官网&#xff0c;点击鞋类&#xff0c;可以看到请求 直接搜sign function p(e) {return f()("".concat(e ? s()(e).sort().reduce(function(t, n) {return "".concat(t).concat(n).concat(e[n])}, "") : "", "048a9…

Ubuntu 安装WPS Office

文章目录 Ubuntu 安装WPS Office下载安装文件安装WPS问题1.下载缺失字体文件2.安装缺失字体 Ubuntu 安装WPS Office 下载安装文件 需要到 WPS官网 下载最新软件&#xff0c;比如wps-office_12.1.0.17900_amd64.deb 安装WPS 执行命令进行安装 sudo dpkg -i wps-office_12.1…

javaSE.判空包装类

判空包装类Optional&#xff0c;这个类可以很有效的处理空指针问题 空指针异常&#x1f447; 特判null&#x1f447; Optional类可以更加优雅地处理这种问题&#x1f447;&#x1f447; ofNullable&#x1f447; isPresent isEmpty &#x1f447; &#x1f447; 包装之后&…

使用 vcpkg 构建支持 HTTPS 的 libcurl 并解决常见链接错误

适用环境&#xff1a;Windows 10/11 Visual Studio 2022 CMake ≥ 3.20 目标读者&#xff1a;希望在 C 项目中轻松调用 HTTPS&#xff08;GET/POST/PUT/DELETE&#xff09;&#xff0c;又被 LNK20xx 链接错误困扰的开发者 目录 为什么选 vcpkg 与 libcurl用 vcpkg 安装带 SS…

ISO26262-浅谈用例导出方法和测试方法

目录 1 摘要2 测试方法3 测试用例导出方法4 测试方法与用例导出方法的差异和联系5 结论 1 摘要 ISO26262定义了测试方法和用例导出方法&#xff0c;共同保证产品的开发质量。但在刚开始学习ISO26262的时候&#xff0c;又不是非常清晰地理解它俩的区别和联系。本文主要对它俩的…

RoBoflow数据集的介绍

https://public.roboflow.com/object-detection&#xff08;该数据集的网址&#xff09; 可以看到一些基本情况 如果我们想要下载&#xff0c;直接点击 点击图像可以看到一些基本情况 可以点击红色箭头所指&#xff0c;右边是可供选择的一些yolo模型的格式 如果你想下载…