CSP-动态规划-最长公共子序列(LCS)

一、动态规划

动态规划(Dynamic Programming,简称DP)主要用于求解可以被分解为相似子问题的复杂问题,特别是在优化问题上表现出色,如最短路径、最大子数组和、编辑距离等。动态规划的核心思想是将原问题分解为较小的子问题,通过解决这些子问题,并将结果存储起来(通常是在一个数组或者哈希表中),以避免重复计算,从而提高效率。

动态规划问题的解决通常遵循以下几个步骤:

  1. 暴力穷举所有答案。
  2. 画出递归树,尝试编写递归函数求解。
  3. 若遍历中存在大量重复计算,使用哈希表缓存数据,之后遍历到相同节点就直接查表。
  4. 表示整个计算过程,观察公式求解顺序,改写成更加高效的迭代形式。

二、动态规划的例子

1.斐波那契数列

2.背包问题

3. 最长公共子序列(LCS)

  • 给定一个无序数组nums=[1,5,2,4,3],找出其中最长的递增的子序列,比如1-2-41-2-3。将问题简化,要求算法只返回最长序列的长度(3)

(1) 暴力枚举

  • 把每个子序列都“找个遍”,并且在遍历过程中实时记录当前子序列的长度
    图片描述

(2) 递归解决方案

  1. 递归函数 L:用于计算以特定元素结尾的最长递增子序列的长度;

    • 基础情形:如果当前考虑的元素是数组的最后一个元素,那么以它结尾的最长递增子序列的长度为 1,因为它自身就构成了一个长度为 1 的递增子序列。
    • 递归步骤:对于非最后一个元素,函数会遍历当前元素之后的所有元素,寻找一个值比当前元素大的元素,这意味着可以形成一个递增的序列。对于每一个这样的元素,函数会递归地计算以那个元素为结尾的最长递增子序列的长度,并将其与当前最大长度比较,更新当前最大长度。这个过程会重复直到数组结束。
    • 返回值:函数最终返回以当前元素结尾的最长递增子序列的长度。
  2. 函数 lengthOfLIS:作用是找到整个数组的最长递增子序列的长度。

    • 遍历给定数组的每个元素,对每个元素调用递归函数 L,计算以该元素为结尾的最长递增子序列的长度。
    • 比较并更新 max_len 为当前找到的最长递增子序列的长度。
    • 遍历完成后,返回 max_len 作为最终结果。
#include <iostream>
#include <vector>
using namespace std;// 计算以 nums[i] 结尾的最长递增子序列的长度
int L(const vector<int>& nums, int i) {if (i == nums.size() - 1) { // 如果是最后一个元素return 1; // 最长递增子序列长度为1}int max_len = 1; // 初始化最大长度为1for (int j = i + 1; j < nums.size(); ++j) {if (nums[j] > nums[i]) { // 如果找到一个递增的元素// 递归计算以 nums[j] 结尾的最长递增子序列长度,并加1(加上nums[i])// 然后与当前的最大长度取较大值max_len = max(max_len, L(nums, j) + 1);}}return max_len; // 返回以 nums[i] 结尾的最长递增子序列的长度
}// 计算给定序列的最长递增子序列长度
int lengthOfLIS(const vector<int>& nums) {int max_len = 0; // 初始化全局最大长度为0for (int i = 0; i < nums.size(); ++i) {// 遍历每个元素,计算以每个元素为起点的最长递增子序列的长度// 然后取所有长度中的最大值max_len = max(max_len, L(nums, i));}return max_len; // 返回最长递增子序列的长度
}int main() {vector<int> nums = {1, 5, 2, 4, 3}; cout << lengthOfLIS(nums) << endl; return 0;
}

(3) 递归的问题

  • 直接递归的方法在时间复杂度上是非常高的,因为它会重复计算很多子问题的解。
  • 比如,在遍历子序列1-2-4时就已经计算过“L(4)”,后面遍历1,4时又重复计算了一次。

(4) 递归的优化:动态规划

  • 为了避免递归中出现的重复计算,可以将第一次计算时的结果保存,之后再当遍历到相同的节点我们就不在需要重复计算,直接返回之前的结果即可。

  • 在这个版本中,L 函数中添加了一个 unordered_map (哈希表)类型的备忘录 memo,用于存储已经计算过的子问题的解。在递归的过程中,先检查备忘录是否已经包含了当前子问题的解,如果有则直接返回保存的结果,避免了重复计算。这样能够显著提高程序的性能。

#include <iostream>
#include <vector>
#include <unordered_map>
using namespace std;// 使用备忘录的递归方式计算以 nums[i] 结尾的最长递增子序列的长度
int L(const vector<int>& nums, int i, unordered_map<int, int>& memo) {if (i == nums.size() - 1) {return 1;}if (memo.find(i) != memo.end()) {return memo[i]; // 如果已经计算过,直接返回保存的结果}int max_len = 1;for (int j = i + 1; j < nums.size(); ++j) {if (nums[j] > nums[i]) {max_len = max(max_len, L(nums, j, memo) + 1);}}memo[i] = max_len; // 将结果保存到备忘录中return max_len;
}// 计算给定序列的最长递增子序列长度
int lengthOfLIS(const vector<int>& nums) {int max_len = 0;unordered_map<int, int> memo; // 使用unordered_map作为备忘录for (int i = 0; i < nums.size(); ++i) {max_len = max(max_len, L(nums, i, memo));}return max_len;
}int main() {vector<int> nums = {1, 5, 2, 4, 3};cout << lengthOfLIS(nums) << endl;return 0;
}

(5) 递归转非递归

  • 从后往前依次计算,即可推算出所有答案(数学归纳)
    图片描述

  • dp 数组:用于存储以每个元素结尾的最长递增子序列的长度。

  • 双重循环:外层循环遍历每个元素,内层循环遍历当前元素之前的元素,更新以当前元素结尾的最长递增子序列的长度。

  • max_element 函数:返回 dp 数组中的最大值,即整个数组中最长递增子序列的长度。

#include <iostream>
#include <vector>
using namespace std;int lengthOfLIS(const vector<int>& nums) {int n = nums.size();if (n == 0) return 0; // 处理空数组的情况vector<int> dp(n, 1); // 初始化dp数组,每个元素代表以对应位置元素结尾的最长递增子序列的长度for (int i = 1; i < n; ++i) {for (int j = 0; j < i; ++j) {if (nums[i] > nums[j]) {dp[i] = max(dp[i], dp[j] + 1); // 更新以nums[i]结尾的最长递增子序列长度}}}return *max_element(dp.begin(), dp.end()); // 返回dp数组中的最大值,即最长递增子序列的长度
}int main() {vector<int> nums = {1, 5, 2, 4, 3}; // 定义一个序列cout << lengthOfLIS(nums) << endl; // 输出最长递增子序列的长度return 0;
}

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

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

相关文章

STM32 USART串口通信

目录 USART串口 串口发送 串口发送接收 串口收发HEX数据包 串口收发文本数据包 USART串口 串口发送 Serial.c #include "stm32f10x.h" // Device header #include "stdio.h" #include "stdarg.h"/*** brief 初始化串口以…

HTML 超文本标记语言

超文本标记语言 HTML 在一个客户程序主窗口上显示出的万维网文档称为页面 (page)。 页面制作的标准语言&#xff1a;HTML。 超文本标记语言 HTML (HyperText Markup Language) 是一种制作万维网页面的标准语言&#xff0c;它消除了不同计算机之间信息交流的障碍&#xff0c…

SQLyog安装配置(注册码)连接MySQL

下载资源 博主给你打包好了安装包&#xff0c;在网盘里&#xff0c;只有几Mb&#xff0c;防止你下载到钓鱼软件 快说谢谢博主&#xff08;然后心甘情愿的点个赞~&#x1f60a;&#xff09; SQLyog.zip 安装流程 ①下载好压缩包后并解压 ②打开文件夹&#xff0c;双击安装包 ③…

GPT 3.5 真的比 4.0聪明吗?

GPT 3.5 真的比 4.0聪明吗&#xff1f; DeepGo 计算机杂谈及深度学习记录&分享 在大语言模型大杀四方的今天 无论是哪个行业的工作人员 都用上了各种各样的模型 其中的佼佼者就是 ChatGPT! 众所周知 ChatGPT是有氪金的Plus4.0版本 那3.5真的不如4.0吗&#xff1f; 今天 我们…

【51单片机】DS18B20(江科大)

一、DS18B20温度传感器 1.DS18B20介绍 DS18B20是一种常见的数字温度传感器,其控制命令和数据都是以数字信号的方式输入输出,相比较于模拟温度传感器,具有功能强大、硬件简单、易扩展、抗干扰性强等特点 测温范围 :- 55℃到125℃ 通信接口:1-Wire(单总线) 其它特征:可形成…

【MySQL进阶之路】亿级数据量表SQL调优实战

欢迎关注公众号&#xff08;通过文章导读关注&#xff1a;【11来了】&#xff09;&#xff0c;及时收到 AI 前沿项目工具及新技术的推送&#xff01; 在我后台回复 「资料」 可领取编程高频电子书&#xff01; 在我后台回复「面试」可领取硬核面试笔记&#xff01; 文章导读地址…

Swift Combine 发布者订阅者操作者 从入门到精通二

Combine 系列 Swift Combine 从入门到精通一 1. Combine核心概念 你只需要了解几个核心概念&#xff0c;就能使用好 Combine&#xff0c;但理解它们非常重要。 这些概念中的每一个都通过通用协议反映在框架中&#xff0c;以将概念转化为预期的功能。 这些核心概念是&#x…

通过增加缓存优化斐波那契递归的冗余计算

一、python 斐波那契数列的递归实现存在大量的冗余计算。例如&#xff0c;为了计算fib(n)&#xff0c;我们需要计算fib(n-1)和fib(n-2)&#xff0c;但是在计算fib(n-1)的过程中&#xff0c;我们又会重复计算fib(n-2)。当n的值很大时&#xff0c;这种冗余计算会消耗大量的计算资…

肿瘤微环境异质性对治疗反应的影响(综述)

Influence of tumour micro-environment heterogeneity on therapeutic response | Nature 肿瘤的形成涉及肿瘤细胞与细胞外基质、肿瘤血管和免疫细胞的共同进化。肿瘤的成功生长和最终转移并不完全取决于肿瘤细胞的基因改变&#xff0c;还取决于这种突变在特定环境中带来的适…

AMD FPGA设计优化宝典笔记(3)控制集

控制集 1 控制集的个数要求 控制集 control set&#xff1a;因为 7 系列 FPGA&#xff0c;一个 slice 只能有一种控制集&#xff08;触发器的使用方式 比如有复位/有时钟使能等等&#xff09;&#xff0c;多了就会分布到不同的 slice 里&#xff0c; 所以代码尽量统一触发器的…

2.12 分支、循环练习

1、选择题 1.1、以下程序的输出结果是 A 。 main() { int k11,k22,k33,x15; if(!k1) x--; else if(k2) if(k3) x4; else x3; printf(“x%d\n”,x); } A x4 B x15 C x14 D x3 解析&#xff1a;if(!k1) x--; 检查 k1 是否为0。因为 k1 的值为1&#xff0c;所…

嵌入式Qt Qt Creator安装与工程介绍

一.Qt概述 什么是Qt&#xff1a;Qt是一个跨平台的C图形用户界面应用程序框架。它为应用程序开发者提供建立图形界面所需的所有功能。它是完全面向对象的&#xff0c;很容易扩展&#xff0c;并且允许真正的组件编程。 二.Qt Creator下载安装 下载地址&#xff1a;Index of /a…

如何编译zlib?

学习文章&#xff1a;windows zlib库编译步骤_nmake 编译 zlib-CSDN博客 记录关键步骤&#xff1a; 打开 执行&#xff1a; 先cd到该目录&#xff1a; C:\Users\xxx\Downloads\zlib-1.2.11\contrib\masmx86 (这是我的zlib源码的下载路径&#xff09; 执行bld_ml32.bat 再…

【每日一题】03 不同路径Ⅱ(DP2)

问题描述 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物。那么从左上…

LeetCode.145. 二叉树的后序遍历

题目 145. 二叉树的后序遍历 分析 上篇文章我们讲了前序遍历&#xff0c;这道题目是后序遍历。 首先要知道二叉树的后序遍历是什么&#xff1f;【左 右 根】 然后利用递归的思想&#xff0c;就可以得到这道题的答案&#xff0c;任何的递归都可以采用 栈 的结构来实现&#…

Python||数据分析之pyecharts 绘图(词云、气泡)

1. echarts 和 Pyecharts 简介 1.1echarts 简介: • echarts 是一个使用 JavaScript 实现的开源可视化库,涵盖各行业图表,满足各种需求。 • echarts 遵循 Apache-2.0 开源协议,免费商用。 • ECharts 最初由百度团队开源,并于 2018 年初捐赠给 Apache 基金会,成为 AS…

【Tauri】(2):使用Tauri应用开发,使用开源的Chatgpt-web应用做前端,使用rust 的candle做后端,本地运行小模型桌面应用

视频演示地址 https://www.bilibili.com/video/BV17j421X7Zc/ 【Tauri】&#xff08;2&#xff09;&#xff1a;使用Tauri应用开发&#xff0c;使用开源的Chatgpt-web应用做前端&#xff0c;使用rust 的candle做后端&#xff0c;本地运行小模型桌面应用 1&#xff0c;做一个免…

ClickHouse--03--数据类型

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 数据类型1. Int2.FloattoFloat32(...) 用来将字符串转换成 Float32 类型的函数toFloat64(...) 用来将字符串转换成 Float64 类型的函数 3.DecimaltoDecimal32(value…

视觉开发板—K210自学笔记(五)

本期我们来遵循其他单片机的学习路线开始去用板子上的按键控制点亮LED。那么第一步还是先知道K210里面的硬件电路是怎么连接的&#xff0c;需要查看第二节的文档&#xff0c;看看开发板原理图到底是按键是跟哪个IO连在一起。然后再建立输入按键和GPIO的映射就可以开始变成了。 …

Linux第48步_编译正点原子的出厂Linux内核源码

编译正点原子的出厂 Linux 内核源码&#xff0c;为后面移植linux做准备。研究对象如下&#xff1a; 1)、linux内核镜像文件“uImage” 路径为“arch/arm/boot”&#xff1b; 2)、设备树文件“stm32mp157d-atk.dtb” 路径为“arch/arm/boot/dts” 3)、默认配置文件“stm32m…