Acwing-基础算法课笔记之动态规划(线性DP)

Acwing-基础算法课笔记之动态规划(线性DP)

  • 一、数字三角形
    • 1、概述
    • 2、闫氏dp分析法
    • 代码示例
  • 二、最长上升子序列
    • 1、概述
    • 2、闫氏dp分析法
    • 3、过程模拟
    • 4、代码演示
  • 三、最长上升子序列强化版
    • 1、概述
    • 2、代码示例
  • 四、最长公共子序列(LCS)
    • 1、定义
      • (1)分解
      • (2)子问题
    • 2、过程模拟
    • 3、代码示例
  • 五、最短编辑距离
    • 1、定义
      • (1)分解
      • (2)子问题
    • 2、过程模拟
    • 3、代码示例

一、数字三角形

1、概述

给定一个如下图所示的数字三角形,从顶部出发,在每一结点可以选择移动至其左下方的结点或移动至其右下方的结点,一直走到底层,要求找出一条路径,使路径上的数字的和最大。
在这里插入图片描述

2、闫氏dp分析法

在这里插入图片描述
在这里插入图片描述

代码示例

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 510;
int n;
int dp[N][N], w[N][N];
int main() {scanf("%d", &n);for (int i = 1; i <= n; i++) {for (int j = 1; j <= i; j++) {scanf("%d", &w[i][j]);}}for (int i = 1; i <= n; i++)dp[n][i] = w[n][i];//因为从底部开始往上寻找,所以将底部的dp先初始化for (int i = n - 1; i >= 1; i--) {for (int j = 1; j <= i; j++) {dp[i][j] = max(dp[i + 1][j] + w[i][j], dp[i + 1][j + 1] + w[i][j]);}}printf("%d", dp[1][1]);return 0;
}

二、最长上升子序列

1、概述

给定一个长度为N的数列A,求数量单调递增的子序列的长度最长是多少。A的任意子序列B可表示为 B = { A k 1 , A k 2 , . . . , A k p } B=\{A_{k_1},A_{k_2},...,A_{k_p}\} B={Ak1,Ak2,...,Akp},其中 k 1 < k 2 < ⋯ < k p k_1<k_2<\cdots<k_p k1<k2<<kp

2、闫氏dp分析法

在这里插入图片描述

3、过程模拟

如何理解所有以第i个数结尾的上升子序列
在这里插入图片描述
例如: 8 8 8为第 i i i个数,则 8 8 8前面的上升子序列为:
3 , 8 3,8 3,8
1 , 8 1,8 1,8
2 , 8 2,8 2,8
1 , 2 , 8 1,2,8 1,2,8

4、代码演示

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n;
int a[N], dp[N];
int main() {scanf("%d", &n);for (int i = 1; i <= n; i++) {scanf("%d", &a[i]);}for (int i = 1; i <= n; i++) {dp[i] = 1;for (int j = 1; j <= i; j++) {if (a[j] < a[i]) {dp[i] = max(dp[i], dp[j] + 1);}}}int ans = 0;for (int i = 1; i <= n; i++) {ans = max(ans, dp[i]);}printf("%d", ans);return 0;
}

三、最长上升子序列强化版

1、概述

由于数据范围比较大,所以只能利用二分的思想先筛选出序列中最大的数,找出该数前的最大上升子序列。

2、代码示例

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int n;
int a[N], dp[N];
int main() {scanf("%d", &n);for (int i = 0; i < n; i++)scanf("%d", &a[i]);int len = 0;dp[0] = -2e9;for (int i = 0; i < n; i++) {int l = 0, r = len;while (l < r) {int mid = l + r + 1 >> 1;if (dp[mid] < a[i])l = mid;else r = mid - 1;}len = max(len, r + 1);dp[r + 1] = a[i];}printf("%d", len);return 0;
}

四、最长公共子序列(LCS)

1、定义

例如:
s 1 : A B C B D A B s_1:ABCBDAB s1:ABCBDAB
s 2 : B D C A B C s_2:BDCABC s2:BDCABC
D [ i ] [ j ] = s 1 D[i][j]=s_1 D[i][j]=s1 i i i个字符与 s 2 s_2 s2 j j j个字符的 L C S LCS LCS
例如: D [ 7 ] [ 6 ] = 4 D[7][6]=4 D[7][6]=4

(1)分解

1、 D [ i ] [ j ] = D [ i − 1 ] [ j − 1 ] + 1 D[i][j]=D[i-1][j-1]+1 D[i][j]=D[i1][j1]+1
2、 D [ i ] [ j ] = m a x ( D [ i − 1 ] [ j ] D [ i ] [ j − 1 ] ) D[i][j]=max\begin{pmatrix} D[i-1][j] \\ D[i][j-1] \end{pmatrix} D[i][j]=max(D[i1][j]D[i][j1])

(2)子问题

D 00 = 0 , D i 0 = 0 , D 0 j = 0 D_{00}=0,D_{i0}=0,D_{0j}=0 D00=0,Di0=0,D0j=0

2、过程模拟

在这里插入图片描述
⋇ \divideontimes 如果两个字符串的末尾字符相同,分析如下:

∙ \bullet D [ i ] [ j ] = D [ i − 1 ] [ j − 1 ] + 1 D[i][j]=D[i-1][j-1]+1 D[i][j]=D[i1][j1]+1

⋇ \divideontimes 如果两个字符串的末尾字符不同需要舍弃两字符串中的其中一个末尾的字符 x x x y y y,分析如下:

∙ \bullet 如果舍弃 s 1 s_1 s1 x x x,则 D [ i ] [ j ] = m a x ( D [ i − 1 ] [ j ] ) D[i][j]=max(D[i-1][j]) D[i][j]=max(D[i1][j])

∙ \bullet 如果舍弃 s 2 s_2 s2 y y y,则 D [ i ] [ j ] = m a x ( D [ i ] [ j − 1 ] ) D[i][j]=max(D[i][j-1]) D[i][j]=max(D[i][j1])

画表格来描述:

1、 D [ i ] [ j ] = D [ i − 1 ] [ j − 1 ] + 1 , s 1 [ i − 1 ] = s 2 [ j − 1 ] D[i][j]=D[i-1][j-1]+1,s_1[i-1]=s_2[j-1] D[i][j]=D[i1][j1]+1,s1[i1]=s2[j1]
2、 D [ i ] [ j ] = m a x ( D [ i − 1 ] [ j ] D [ i ] [ j − 1 ] ) , s 1 [ i − 1 ] ! = s 2 [ j − 1 ] D[i][j]=max\begin{pmatrix} D[i-1][j] \\ D[i][j-1] \end{pmatrix},s_1[i-1]!=s_2[j-1] D[i][j]=max(D[i1][j]D[i][j1]),s1[i1]!=s2[j1]

∅ \varnothing BDCABC
∅ \varnothing 0000000
A0
B0
C0
B0
D0
A0
B0

⇓ \Darr

∅ \varnothing BDCABC
∅ \varnothing 0000000
A0000111
B0111122
C0112223
B0112233
D0122233
A0122333
B0122344

方法: 看坐标 ( i , j ) (i,j) (i,j)的元素是否相等,如果相等则以左斜上方的数为基础加 1 1 1,否则等于左边的数。

如果不理解可以看一看这位大佬的视频

3、代码示例

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n, m;
int dp[N][N];
char a[N], b[N];
int main() {cin >> n >> m;cin >> a + 1;cin >> b + 1;for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {if (a[i] == b[j])dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);}}cout << dp[n][m];return 0;
}

五、最短编辑距离

1、定义

D [ i ] [ j ] D[i][j] D[i][j]等于 s 1 s_1 s1 i i i个字符编辑为 s 2 s_2 s2的前 j j j个字符它的编辑距离。
例如:
s u n sun sun编辑为 s a t u r satur satur,则 D 35 = 3 D_{35}=3 D35=3

(1)分解

⋇ \divideontimes 如果 s 1 s_1 s1 s 2 s_2 s2的末尾字符相同,则只要编辑 s i − 1 s_{i-1} si1的字符串变成 s j − 1 s_{j-1} sj1的字符串的次数
在这里插入图片描述
∙ \bullet 如果 s i − 1 s_{i-1} si1等于 s j − 1 s_{j-1} sj1,则 D [ i ] [ j ] = D [ i − 1 ] [ j − 1 ] D[i][j]=D[i-1][j-1] D[i][j]=D[i1][j1]

⋇ \divideontimes 如果 s 1 s_1 s1 s 2 s_2 s2的末尾字符不相同
在这里插入图片描述
∙ \bullet 如果 s 1 s_1 s1的字符串末尾与 s 2 s_2 s2相比少了一个 y y y,则在 s 1 s_1 s1的末尾插入 y y y,则状态转移方程为: D [ i ] [ j ] = D [ i ] [ j − 1 ] + 1 D[i][j]=D[i][j-1]+1 D[i][j]=D[i][j1]+1

∙ \bullet 如果 s 1 s_1 s1的字符串末尾与 s 2 s_2 s2相比多了一个 x x x,则删除 s 1 s_1 s1末尾的 x x x,则状态转移方程为: D [ i ] [ j ] = D [ i − 1 ] [ j ] + 1 D[i][j]=D[i-1][j]+1 D[i][j]=D[i1][j]+1

∙ \bullet 如果 s 1 s_1 s1 s 2 s_2 s2具有高度的相似性,则将 s 1 s_1 s1当中的某个字符替换成 s 2 s_2 s2当中的某个字符,则状态转移方程为: D [ i ] [ j ] = D [ i − 1 ] [ j − 1 ] + 1 D[i][j]=D[i-1][j-1]+1 D[i][j]=D[i1][j1]+1

在这三种条件中选最小

(2)子问题

D 00 = 0 , D i 0 = i , D 0 j = j D_{00}=0,D_{i0}=i,D_{0j}=j D00=0,Di0=i,D0j=j

2、过程模拟

在这里插入图片描述

∅ \varnothing satur
∅ \varnothing 012345
s1
u2
n3

⇓ \Darr

∅ \varnothing satur
∅ \varnothing 012345
s101234
u211223
n322233

方法: 如果所在同一坐标的两个字符相等,则该坐标的值等于左上方坐标的值。如果不相等,则需要考虑是插入、删除还是替换。如果是插入,则当前坐标的值等于左边坐标的值加 1 1 1。如果是删除,则当前坐标的值等于上方的值加 1 1 1。如果是替换,则当前坐标的值等于左上方的值加 1 1 1

3、代码示例

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int n, m;
char a[N], b[N];
int dp[N][N];
int main() {scanf("%d%s", &n, a + 1);scanf("%d%s", &m, b + 1);for (int i = 1; i <= n; i++)dp[i][0] = i;for (int j = 1; j <= m; j++)dp[0][j] = j;for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {dp[i][j] = min(dp[i][j - 1] + 1, dp[i - 1][j] + 1);if (a[i] == b[j])dp[i][j] = min(dp[i][j], dp[i - 1][j - 1]);else dp[i][j] = min(dp[i - 1][j - 1] + 1, dp[i][j]);}}printf("%d\n", dp[n][m]);return 0;
}

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

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

相关文章

FFmpeg查看所有支持的编码/解码器/封装/解封装/媒体格式/滤镜

查看所有支持的编码器与解码器 ffmpeg -codecs 只查看所有编码器: ffmpeg -encoders 只查看所有解码器: ffmpeg -decoders 只查看H264编码器: ffmpeg -h encoderh264 只查看H264解码器: ffmpeg -h decoderh264 查看所有支持的封装: ffmpeg -muxers 查看所有支持的解封装…

三.使用java的API文档

在Java中&#xff0c;API是指“应用程序接口”&#xff08;Application Programming Interface&#xff09;。Java API是Java编程语言中提供的类和接口的集合&#xff0c;用于开发各种类型的应用程序。类比C的STL&#xff08;标准模板库&#xff09;。 通俗理解就当做些封装好…

Spark-Scala语言实战(1)

在之前的文章中&#xff0c;我们学习了如何在Linux安装Spark以及Scala&#xff0c;想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请留下你宝贵的点赞&#xff0c;谢谢。 Spark及Scala的安装https:/…

如何在Windows11上安装WSL和Linux子系统以及搭建Docker环境

今天给大家介绍一下如何在Windows11上安装Docker 打开控制面板&#xff1a; 打开程序&#xff1a; 打开启用或关闭Windows功能。 勾选Linux子系统&#xff1a; 此时&#xff0c;可能需要重启电脑。 以管理员身份打开PowerShell执行&#xff1a; bcdedit /set hyperv…

C语言学习过程总结(18)——指针(6)

一、数组指针变量 在上一节中我们提到了&#xff0c;指针数组的存放指针的数组&#xff0c;那数组指针变量是什么呢&#xff1f; 显而易见&#xff0c;数组指针变量是指针 同样类比整型指针变量和字符指针变量里面分别存放的是整型变量地址和字符变量地址&#xff0c;我们可以…

layui table列表重载后保持进度条位置不变

使用layui的table表格组件时&#xff0c;当我们操作了某行的修改后&#xff0c;刷新了页面&#xff0c;进度条则跳回到最上面。 除了layui高版本应该内置有方法解决了此问题&#xff0c;但是低版本需要另外想办法解决。 具体解决方式如下&#xff1a; 1.在编辑操作成功前&am…

redis中List和hash数据类型

list类型是用来存储多个有序的字符串的&#xff0c;列表当中的每一个字符看做一个元素&#xff0c;一个列表当中可以存储一个或者多个元素&#xff0c;redis的list支持存储2^32-1个元素。redis可以从列表的两端进行插入&#xff08;pubsh&#xff09;和弹出&#xff08;pop&…

springboot多模块下swaggar界面出现异常(Knife4j文档请求异常)或者界面不报错但是没有显示任何信息

继上一篇博文&#xff0c;我们解决了多模块下扫描不到子模块的原因,建议先看上一个博客了解项目结构&#xff1a; springboot 多模块启动报错Field XXX required a bean of type XXX that could not be found. 接下来我们来解决swaggar异常的原因&#xff0c;我们成功启动项目…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:SideBarContainer)

提供侧边栏可以显示和隐藏的侧边栏容器&#xff0c;通过子组件定义侧边栏和内容区&#xff0c;第一个子组件表示侧边栏&#xff0c;第二个子组件表示内容区。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起…

Vue2在一个页面内动态切换菜单显示对应的路由组件

项目的需求是在一个页面内动态获取导航菜单&#xff0c;导航菜单切换的时候显示对应的路由页面&#xff0c;类似于tab切换的形式&#xff0c;切换的导航菜单和页面左侧导航菜单是同一个路由组件&#xff0c;只是放到了一个页面上&#xff0c;显示的个数不同&#xff0c;所有是动…

【C语言初阶(五)】数组

❣博主主页: 33的博客❣ ▶文章专栏分类: C语言从入门到精通◀ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; 目录 1. 前言2.一维数组的概念3.一维数组的创建和初始化3.1数组的创建3.2数组的初始化3.3数组的类型 4.一维数组的使用4.1数组下标4.2数组元素打印4.4数组元…

智慧城市革命,物联网技术如何改变城市治理与生活方式

随着科技的不断进步&#xff0c;智慧城市已经成为现代城市发展的重要方向之一。物联网技术作为智慧城市的重要支撑&#xff0c;正深刻改变着城市的治理模式和居民的生活方式。本文将探讨智慧城市革命&#xff0c;以及物联网技术如何改变城市治理与生活方式&#xff0c;同时介绍…

OpenCV系列文章目录(持续更新中......)

引言&#xff1a; OpenCV是一个开源的计算机视觉库&#xff0c;由英特尔公司开发并开源的一组跨平台的C函数和少量的C函数组成&#xff0c;用于实时图像处理、计算机视觉和机器学习等应用领域。OpenCV可以在包括Windows、Linux、macOS等各种操作系统平台上使用&#xff0c;具…

flink1.18.0 自定义函数 接收row类型的参数

比如sql中某字段类型 array<row<f1 string,f2 string,f3 string,f4 bigint>> 现在需要编写 tableFunction 需要接受的参数如上 解决方案 用户定义函数|阿帕奇弗林克 --- User-defined Functions | Apache Flink

iPhone 的健康数据采用的是 FHIR 传输格式

虽然感觉 FHIR 的数据传输格式还是有点繁琐的&#xff0c;但貌似现在也是唯一的事实上的标准。 通过 iPhone 健康上面查看的数据来看&#xff0c;有关健康的数据还是使用 FHIR 的数据传输格式。 不管怎么样&#xff0c;针对老旧的数据传输格式来看&#xff0c;FHIR 至少目前还是…

实现HBase表和RDB表的转化(附Java源码资源)

实现HBase表和RDB表的转化 一、引入 转化为HBase表的三大来源&#xff1a;RDB Table、Client API、Files 如何构造通用性的代码模板实现向HBase表的转换&#xff0c;是一个值得考虑的问题。这篇文章着重讲解RDB表向HBase表的转换。 首先&#xff0c;我们需要分别构造rdb和hba…

瑞_Redis_短信登录(二)

文章目录 项目介绍1.1 项目准备1.2 基于Session实现登录流程1.2.1 发送短信验证码1.2.2 短信验证码登录、注册1.2.3 校验登录状态 1.3 实现发送短信验证码功能1.3.1 页面流程1.3.2 代码实现 1.41.51.6 &#x1f64a; 前言&#xff1a;本文章为瑞_系列专栏之《Redis》的实战篇的…

Redis和Mysql的数据一致性问题

在高并发的场景下&#xff0c;大量的请求直接访问Mysql很容易造成性能问题。所以我们都会用Redis来做数据的缓存&#xff0c;削减对数据库的请求的频率。 但是&#xff0c;Mysql和Redis是两种不同的数据库&#xff0c;如何保证不同数据库之间数据的一致性就非常关键了。 1、导…

Linux自动化任务管理以及常见定时命令示例

Linux以其强大的稳定性和灵活性成为了许多IT专业人士的首选。其中&#xff0c;自动化任务管理是Linux系统管理不可或缺的一部分&#xff0c;它能帮助系统管理员有效地管理系统任务&#xff0c;提高工作效率。定时任务&#xff0c;作为自动化任务管理的重要组成部分&#xff0c;…

Go——运算符,变量和常量,基本类型

一.运算符 Go语言内置的运算符有&#xff1a; 算术运算符 关系运算符 逻辑运算符 位运算符 赋值运算符 1.1 算术运算符 注意&#xff1a;(自增)和--(自减)在go语言中是单独的语句&#xff0c;并不是运算符。 1.2 关系运算符 1.3 逻辑运算符 1.4 位运算符 位运算符对整数在内存…