当前位置: 首页 > news >正文

Acwing 线性DP

状态转移方程呈现出一种线性的递推形式的DP,我们将其称为线性DP。

Acwing 898.数字三角形

在这里插入图片描述
实现思路:

  • 对这个三角形的数字进行编号,状态表示依然可以用二维表示,即f(i,j),i表示横坐标(横线),j表示纵坐标(斜线)
    在这里插入图片描述
    在这里插入图片描述
  • f(i,j)表示到点(i,j)的路径最大数字之和。对集合进行划分,到达某点(i,j)只可能经过左上方的点(i-1,j-1)或右上方的点(i-1,j)。用a[i][j]表示当前点的数值;
  • 故可得状态转移方程:f[i][j]=max(f[i-1][j-1]+a[i][j],f[i-1][j]+a[i][j])

具体实现代码(详解版):

#include <iostream>
#include <algorithm>using namespace std;const int N = 510, INF = 1e9;  
int n; 
int a[N][N];  // 存储三角形中的数字
int f[N][N];  // 动态规划数组,存储从顶点到达每个位置的最大路径和int main() {cin >> n;  for (int i = 1; i <= n; i++) {for (int j = 1; j <= i; j++) {cin >> a[i][j];  }}// 初始化 DP 数组,将所有 f[i][j] 初始化为一个极小值,表示不可到达for (int i = 0; i <= n; i++) {for (int j = 0; j <= i + 1; j++) {f[i][j] = -INF;}}// 设置起始点,顶部元素的最大路径和就是它自身f[1][1] = a[1][1];// 状态转移方程:f[i][j] 表示到达第 i 行第 j 列的最大路径和for (int i = 2; i <= n; i++) {for (int j = 1; j <= i; j++) {// 到达 f[i][j] 位置有两种可能:// 1. 来自 f[i-1][j-1],即从左上角过来// 2. 来自 f[i-1][j],即从正上方过来f[i][j] = max(f[i - 1][j - 1] + a[i][j], f[i - 1][j] + a[i][j]);}}// 在最后一行的所有位置中找出最大值int res = -INF;for (int i = 1; i <= n; i++) {res = max(res, f[n][i]);  // 找出最后一行的最大路径和}cout << res << endl;  return 0;
}

这道题还可以从下往上递推,考虑f[i][j]来自左下方和来自右下方两种情况,这样就不需要处理边界问题,而且最后的结果一定集中在f[1][1]中。

#include <iostream>
#include <algorithm>
using namespace std;const int N = 510, INF = 1e9;  // 定义常量 N 为最大行数,INF 为极大值
int n;  // 表示三角形的行数
int f[N][N], a[N][N];  // f 用于存储从底部到达每个位置的最大路径和,a 存储三角形中的数字int main() {int n;cin >> n;  // 输入三角形的行数// 输入三角形中的数字for (int i = 1; i <= n; i++)for (int j = 1; j <= i; j++)cin >> a[i][j];// 初始化:将最后一行的值作为初始状态for (int i = 1; i <= n; i++) f[n][i] = a[n][i];// 自底向上递推,计算每一行的最大路径和for (int i = n - 1; i >= 1; i--) {for (int j = 1; j <= i; j++) {// f[i][j] 表示在 (i, j) 位置的最大路径和f[i][j] = max(f[i + 1][j], f[i + 1][j + 1]) + a[i][j];}}// 输出顶点处的最大路径和cout << f[1][1] << endl;return 0;
}

Acwing 895.最长上升子序列

在这里插入图片描述
实现思路:
在这里插入图片描述

  • 一维数组f[i]表示以第i个数为结尾的最长递增子序列的长度;
  • 状态划分:选定i为结尾的递增子序列,则再从[0,i-1]中筛选出倒数第二个位置的数,使递增子序列的长度最大。注意这个倒数第二个位置的数必须满足a[j]<a[i],这样才能保证递增序列
  • 状态转移方程为f[i]=max(f[i],f[j]+1);

具体实现代码(详解版):

#include <iostream>
#include <algorithm>using namespace std;const int N = 1010; int n; 
int a[N], f[N];  // a 存储输入的数组, f 存储以每个元素结尾的最长上升子序列的长度int main(){cin >> n;  for(int i = 1 ; i <= n ; i ++) cin >> a[i];  // 动态规划计算最长上升子序列for(int i = 1 ; i <= n ; i ++) {f[i] = 1;  // 初始状态,每个元素自身可以作为一个长度为1的子序列for(int j = 1 ; j < i ; j ++){// 如果前面的元素 a[j] 比当前元素 a[i] 小,则可以考虑将 a[i] 接在 a[j] //之后形成一个更长的子序列if(a[j] < a[i]) f[i] = max(f[i], f[j] + 1);  // 更新 f[i],选择使 f[i] 最大的方案}}// 找到 f 数组中的最大值,即最长上升子序列的长度int res = 0;for(int i = 1 ; i <= n ; i ++) res = max(res, f[i]);cout << res << endl;  return 0;
}

那有没有办法进行优化呢?最长上升子序列(LIS)问题的时间复杂度为 O ( n 2 ) , O(n^2), O(n2),我们可以通过贪心算法 + 二分查找来将时间复杂度优化为 O ( n l o g n ) O(nlogn) O(nlogn).

Acwing 896. 最长上升子序列 II

在这里插入图片描述
实现思路:

  • 首先在上述解法的基础上,假如存在一个序列3 1 2 5,以3结尾的上升子序列长度为1,以1为结尾的上升子序列长度也为1,这是两个长度一样的上升子序列(伏笔:结尾元素1<3)。在继续向后遍历查找时,看3这个序列,当出现一个比3大的数时,以3结尾的上升子序列就会更新,比如遍历到5了,那么上升序列变为3 5;同时注意到这个5一定会加入到以1结尾的上升序列中(因为1<3,那么1<5的),那么含有1的上升序列长度一定是>=2的,因为中间可能存在<3但>1的数(比如这里就有2,序列长度就更新为3)。可以看出存在3的这个序列就不需要枚举了,因为存在1的序列往后遍历的长度是一定大于你这个存在3的序列的(前提是以1结尾和以3结尾的上升序列长度相等),那我找最长的时候怎么都轮不到包含3的序列头上,那我一开始在1和3结尾的序列之后直接舍弃枚举包含3的序列了(去掉冗余)。
  • 在以上的分析得到:当存在两个上升序列长度相同时,结尾数更大的序列可以舍去不再枚举,所以每次就干脆选出相同长度结尾元素最小的序列继续操作
  • 那么状态表示更改为:f[i]表示长度为i+1(因为下标从0开始)的最长上升子序列,末尾最小的数字。(所有长度为i+1的最长上升子序列所有结尾中,结尾最小的数) 即长度为i的子序列末尾最小元素是什么。
  • 状态计算:序列长度+1(cnt++),当前末尾最小元素变为a[i]。 **若a[i]小于等于f[cnt-1],**说明不会更新当前的长度,但之前末尾的最小元素要发生变化,找到第一个 大于或等于(不能直接写大于,要保证单增) a[i]的数的位置mid,将这个数a[i]放在mid的位置(其实就是找到a[i]适合存在的位置,不改变序列长度)。

具体实现代码(版本一):

#include <iostream>
#include <vector>
#include <algorithm>using namespace std;const int N = 100010;  // 定义最大数组大小
int n, cnt;
int a[N], f[N];int main() {// 输入数组长度cin >> n;// 输入数组元素for (int i = 0; i < n; i++) {cin >> a[i];}// 初始化 cntcnt = 0;// 处理第一个元素f[cnt++] = a[0];for (int i = 1; i < n; i++) { // 注意这里应为 i < nif (a[i] > f[cnt - 1]) {f[cnt++] = a[i]; // 如果 a[i] 大于当前上升序列末尾的数,则末尾加入} else {// 使用二分查找int l = 0, r = cnt - 1;while (l < r) {int mid = (l + r) / 2;if (f[mid] >= a[i]) r = mid; // 找到第一个 >= a[i] 的位置else l = mid + 1;}f[r] = a[i]; // 替换找到的位置}}cout << cnt << endl; // 输出最长上升子序列的长度return 0;
}

版本二(使用lower_bound函数,找到第一个 >= a[i] 的位置)

#include <iostream>
#include <vector>
#include <algorithm>using namespace std;const int N = 100010;  // 定义最大数组大小
int n;
int a[N];int main() {// 输入数组长度cin >> n;// 输入数组元素for (int i = 0; i < n; i++) {cin >> a[i];}// 用于维护当前的上升子序列vector<int> d;// 遍历数组的每个元素for (int i = 0; i < n; i++) {// 使用二分查找找到第一个 >= a[i] 的位置auto it = lower_bound(d.begin(), d.end(), a[i]);if (it == d.end()) {// 如果没有找到比 a[i] 大的元素,则将 a[i] 添加到序列末尾d.push_back(a[i]);} else {// 否则,用 a[i] 替换掉找到的这个位置的元素*it = a[i];}}// 最终 d 的大小就是最长上升子序列的长度cout << d.size() << endl;return 0;
}

Acwing 897. 最长公共子序列

在这里插入图片描述
实现思路:
在这里插入图片描述

  • f(i,j)表示第一个序列的前i个字母中出现并且在第二序列前j个字母中出现的最长的公共子序列长度
  • 状态可划分为4种情况:a[i]表示为第一个序列中第i个字符,b[j]表示第二个子序列中第j个字符
    • 00:表示最长公共子序列中一定不包含字符a[i]和b[j],用f[i-1][j-1]表示
    • 01:表示最长公共子序列中一定不包含字符a[i],一定包含b[j]。不能用f[i-1][j]表示(不是等价的),因为f[i-1][j]表示的是该公共子序列一定不包含a[i],但b[j]不一定,可能包含也可能不包含f[i-1][j]是包含01这种情况的。但是由于求的是最大子序列的长度(而不是具体元素),所以求解的时候可以用f[i-2][j]来求解
    • 10:表示最长公共子序列中一定包含字符a[i],一定不包含b[j]。用f[i][j-1]求解,但含义不等价,同上。
    • 11:表示最长公共子序列中一定包含字符a[i]和b[j],用f[i-1][j-1]+1表示,但注意需要满足a[i] = b[j]才行,因为公共子序列,既然包含a[i]、b[i],那么两者必然相等才行
  • 00的情况实质上已经被包含在01、10两种情况之中,所以可以省略,故只需求下面三种状态

具体实现代码(详解版):

#include <iostream>
#include <algorithm>using namespace std;const int N = 1010;char a[N], b[N];
int f[N][N];
int n, m;int main() {cin >> n >> m;// 输入字符串,保留 a[0] 和 b[0] 为空字符cin >> (a + 1) >> (b + 1);// 动态规划计算最长公共子序列for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {// 如果字符相同,则长度加 1if (a[i] == b[j]) {f[i][j] = f[i - 1][j - 1] + 1;} else {// 否则取上方或左方的最大值f[i][j] = max(f[i - 1][j], f[i][j - 1]);}}}// 输出最长公共子序列的长度cout << f[n][m] << endl;return 0;
}

相关文章:

Acwing 线性DP

状态转移方程呈现出一种线性的递推形式的DP&#xff0c;我们将其称为线性DP。 Acwing 898.数字三角形 实现思路&#xff1a; 对这个三角形的数字进行编号&#xff0c;状态表示依然可以用二维表示&#xff0c;即f(i,j),i表示横坐标&#xff08;横线&#xff09;&#xff0c;j表…...

Docker面试-24年

1、Docker 是什么&#xff1f; Docker一个开源的应用容器引擎&#xff0c;是实现容器技术的一种工具&#xff0c;让开发者可以打包他们的应用以及环境到一个镜像中&#xff0c;可以快速的发布到任何流行的操作系统上。 2、Docker的三大核心是什么? 镜像&#xff1a;Docker的…...

ubuntu 安装k8s

#关闭 Swap 内存&#xff0c;配置完成建议重启一下 nano /etc/fstab #注释下面相似的一行 #/swapfile none swap sw 0 0 #重启 reboot#部属k8s apt update && apt install -y apt-transport-https 下载 gpg 密钥 curl https://mi…...

No.4 笔记 | 探索网络安全:揭开Web世界的隐秘防线

在这个数字时代&#xff0c;网络安全无处不在。了解Web安全的基本知识&#xff0c;不仅能保护我们自己&#xff0c;也能帮助我们在技术上更进一步。让我们一起深入探索Web安全的世界&#xff0c;掌握那些必备的安全知识&#xff01; 1. 客户端与WEB应用安全 前端漏洞&#xff1…...

spring揭秘24-springmvc02-5个重要组件

文章目录 【README】【1】HanderMapping-处理器映射容器【1.1】HanderMapping实现类【1.1.1】SimpleUrlHandlerMapping 【2】Controller&#xff08;二级控制器&#xff09;【2.1】AbstractController抽象控制器&#xff08;控制器基类&#xff09; 【3】ModelAndView(模型与视…...

关键字:register

1.铺垫 1.1 计算集中具有存储能力的硬件&#xff1a;cpu中的寄存器、cache&#xff0c;内存&#xff0c;硬盘等 1.2离cpu越近的存储硬件&#xff0c;效率越高&#xff0c;单价成本越贵&#xff1b;离cpu越远的存储硬件&#xff0c;效率越低&#xff0c;单价成本越便宜&#x…...

力扣 简单 110.平衡二叉树

文章目录 题目介绍解法 题目介绍 解法 平衡二叉树:任意节点的左子树和右子树的高度之差的绝对值不超过 1 //利用递归方法自顶向下判断以每个节点为根节点的左右子树的最大深度是否大于1 class Solution {public boolean isBalanced(TreeNode root) {if(root null){return tr…...

基于深度学习的代码优化

基于深度学习的代码优化是一种使用深度学习技术来提升编程代码性能、减少运行时间或资源消耗的方式。通过模型学习大量代码的特征和结构&#xff0c;深度学习可以帮助自动化地识别和应用优化策略。以下是一些关键应用领域&#xff1a; 编译器优化&#xff1a;深度学习模型可以用…...

汽车电气系统中KL30、KL15、KL50、KLR、KL31、KL87、KL75的作用

目录 1、KL30 (Battery Positive Terminal) 2、KL15 (Ignition Switch, Positive) 3、KL50 (Starter Motor Terminal) 4、KLR (Ignition-Off Draw) 5、KL31 (Ground) 6、KL87 (Relay Output) 7、KL75 (Accessory) 在汽车电气系统中&#xff0c;KL系列的术语起源于德国&a…...

随笔(四)——代码优化

文章目录 前言1.原本代码2.新增逻辑3.优化逻辑 前言 原逻辑&#xff1a;后端data数据中返回数组&#xff0c;数组中有两个对象&#xff0c;一个是属性指标&#xff0c;一个是应用指标&#xff0c;根据这两个指标展示不同的多选框 1.原本代码 getIndicatorRange(indexReportLi…...

安装管理K8S的开源项目KubeClipper介绍

安装管理K8S的开源项目KubeClipper介绍 1. 概述 KubeClipper是九州云开源的一个图形化界面 Kubernetes 多集群管理工具&#xff0c;旨在提供易使用、易运维、极轻量、生产级的 Kubernetes 多集群全生命周期管理服务。让运维工程师从繁复的配置和晦涩的命令行中解放出来&#…...

北交大研究突破:塑料光纤赋能低成本无摄像头AR/VR眼动追踪技术

北交大研究&#xff1a;探索无摄像头低成本AR/VR眼动追踪新路径 在AR/VR技术领域&#xff0c;眼动追踪作为一项关键技术&#xff0c;对于提升用户体验、优化渲染效率具有重要意义。然而&#xff0c;传统的眼动追踪方案多依赖于高成本的摄像头&#xff0c;这不仅增加了设备的制造…...

算法题总结(七)——哈希表

当我们遇到了要快速判断一个元素是否出现集合里的时候&#xff0c;就要考虑哈希法 242、有效地字母异位词 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 t 中每个字符出现的次数都相同&#xff0c;则称 s 和 t…...

PS批量执行动作,ps批量修改图片大小,并修改文件的类型

PS批量执行动作&#xff0c;ps批量修改图片大小&#xff0c;并修改文件的类型 修改格式&#xff0c;文件类型为&#xff1a;jpg&#xff0c;psd&#xff0c;tiff&#xff0c;并修改大小 打开文件&#xff08;也可以不打开&#xff0c;&#xff09; 点击文件>脚本>文件…...

CentOS 替换 yum源 经验分享

视频教程在bilibili:CentOS 替换 yum源 经验分享_哔哩哔哩_bilibili问题原因 解决方法 1. 进入镜像目录 [rootlocalhost ~]# cd /etc/yum.repos.d/ 2.备份文件 [rootlocalhost yum.repos.d]# rename repo bak * 3.寻找阿里镜像源复制 https://developer.aliyun.com/mirror/ …...

Elasticsearch基础_2.数据类型

文章目录 一、基本的数据类型1.1、keyword1.2、text1.3、数值类型1.4、布尔类型1.5、时间类型 二、复杂的数据类型三、字段映射 一、基本的数据类型 1.1、keyword keyword类型是不进行切分的字符串类型。这里的“不进行切分”指的是&#xff1a;在索引时&#xff0c;对keyword…...

docker快速安装ELK

一、创建elk目录 创建/elk/elasticsearch/data/目录 mkdir -p /usr/local/share/elk/elasticsearch/data/ 创建/elk/logstash/pipeline/目录 mkdir -p /usr/local/share/elk/logstash/pipeline/ 创建/elk/kibana/conf/目录 mkdir -p /usr/local/share/elk/kibana/conf/ 二、创建…...

GS-SLAM论文阅读笔记-CaRtGS

前言 这篇文章看起来有点像Photo-slam的续作&#xff0c;行文格式和图片类型很接近&#xff0c;而且貌似是出自同一所学校的&#xff0c;所以推测可能是Photo-slam的优化与改进方法&#xff0c;接下来具体看看改进了哪些地方。 文章目录 前言1.背景介绍GS-SLAM方法总结 2.关键…...

15分钟学 Python 第36天 :Python 爬虫入门(二)

Python 爬虫入门&#xff1a;环境准备 在进行Python爬虫的学习和实践之前&#xff0c;首先需要准备好合适的开发环境。本节将详细介绍Python环境的安装、必要库的配置、以及常用工具的使用&#xff0c;为后续的爬虫编写奠定坚实的基础。 1. 环境准备概述 1.1 为什么环境准备…...

Spring:强制登陆与拦截器

1.只使用session验证 &#xff08;1&#xff09;第一步&#xff1a;用户登陆时存储session ApiOperation("用户登陆") PostMapping("/login") public AppResult login(HttpServletRequest request,RequestParam("username") ApiParam("用…...

MySQL-数据库约束

1.约束类型 类型说明NOT NULL非空约束 指定非空约束的列不能存储NULL值 DEFAULT默认约束当没有给列赋值时使用的默认值UNIQUE唯一约束指定唯一约束的列每行数据必须有唯一的值PRIMARY KEY主键约束NOT NULL和UNIQUE的结合&#xff0c;可以指定一个列霍多个列&#xff0c;有助于…...

线性表三——队列queue

#include<bits/stdc.h> using namespace std; int n,m; queue<int> q;int main(){cin>>n>>m;for(int i1;i<n;i) q.push(i);int k0;while(!q.empty()){k;if(k<m)//从队头出来&#xff0c;再次回到队尾{int idq.front();//记录出去的编号 q.pop();…...

算法笔记(十)——队列+宽搜

文章目录 N 叉数的层序遍历二叉树的锯齿形层序遍历二叉树最大宽度在每个树行中找最大值 BFS是图上最基础、最重要的搜索算法之一&#xff1b; 每次都尝试访问同一层的节点如果同一层都访问完了&#xff0c;再访问下一层 BFS基本框架 void bfs(起始点) {将起始点放入队列中;标记…...

webpack配置全面讲解【完整篇】

文章目录 前言webpack 核心包&#xff1a;配置文件导出三种方式&#xff1a;在线配置 webpack配置文件解析&#xff1a;入口&#xff08;Entry&#xff09;&#xff1a;输出&#xff08;Output&#xff09;&#xff1a;加载器&#xff08;Loaders&#xff09;&#xff1a;插件&…...

十、kotlin的协程

协程 基本概念定义组成挂起和恢复结构化并发协程构建器作用域构建器挂起函数阻塞与非阻塞runBlocking全局协程像守护线程 Job的生命周期 常用函数延时和等待启动和取消启动取消 暂停 协程启动调度器启动方式启动模式线程上下文继承的定义继承的公式 协程取消与超时取消挂起点取…...

vscode qt 最新开发环境配置, 基于最新插件 Qt All Extensions Pack

qt 之前发布了vscode qt offical ,但是最新更新中将其升级改为了几个不同的插件&#xff0c;功能更强大 1. 前置条件 qt 已安装 2. 插件安装 打开vscode 插件安装&#xff0c;搜索qt 会看到很多qt插件&#xff0c;直接选择Qt All Extensions Pack 安装 会安装qt环境所需的…...

【MySQL】Ubuntu环境下MySQL的安装与卸载

目录 1.MYSQL的安装 2.MySQL的登录 3.MYSQL的卸载 4.设置配置文件 1.MYSQL的安装 首先我们要看看我们环境里面有没有已经安装好的MySQL 我们发现是默认是没有的。 我们还可以通过下面这个命令来确认有没有mysql的安装包 首先我们得知道我们当前的系统版本是什么 lsb_…...

C# StringBuilder类:高效构建和修改字符串的利器

C# 中的 StringBuilder 类是一个可变的字符序列&#xff0c;用于高效地构建和修改字符串。与字符串&#xff08;string&#xff09;不同&#xff0c;字符串在 C# 中是不可变的&#xff0c;这意味着每次修改字符串&#xff08;如拼接、替换等操作&#xff09;时&#xff0c;都会…...

AVL平衡树(AVL Tree)

**场景&#xff1a;课堂讨论** --- **小明&#xff08;ESFP学生&#xff09;**&#xff1a;张老师&#xff0c;为什么AVL树&#xff08;AVL Tree&#xff09;中的旋转操作这么重要&#xff1f;感觉只是节点的移动&#xff0c;有没有什么实际意义&#xff1f; **张老师&#…...

【python实操】python小程序之两数取大值以及login登录

引言 python小程序之两数取大值以及login登录 文章目录 引言一、两数取大值1.1 题目1.2 代码1.3 代码解释 二、login登录2.1 题目2.2 代码2.3 代码解释 三、思考3.1 两数取大值3.2 login登录 一、两数取大值 1.1 题目 定义一个函数my_max&#xff0c;包含两个参数, 函数的作用…...