NO.85十六届蓝桥杯备战|动态规划-经典线性DP|最长上升子序列|合唱队形|最长公共子序列|编辑距离(C++)

news2025/4/18 1:02:01

经典线性dp问题有两个:最⻓上升⼦序列(简称:LIS)以及最⻓公共⼦序列(简称:LCS),这两道题⽬的很多⽅⾯都是可以作为经验,运⽤到别的题⽬中。⽐如:解题思路,定义状态表⽰的⽅式,推到状态转移⽅程的技巧等等。
因此,这两道经典问题是⼀定需要掌握的

B3637 最长上升子序列 - 洛谷
  1. 状态表⽰
    dp[i]表⽰:以i 位置元素为结尾的「所有⼦序列」中,最⻓递增⼦序列的⻓度。
    最终结果就是整张dp 表⾥⾯的最⼤值。
  2. 状态转移⽅程:
    对于dp[i] ,我们可以根据「⼦序列的构成⽅式」,进⾏分类讨论:
  • ⼦序列⻓度为1 :只能⾃⼰玩了,此时dp[i] = 1
  • ⼦序列⻓度⼤于1 :a[i]可以跟在前⾯某些数后⾯形成⼦序列。设前⾯的某⼀个数的下标为j,其中 1 ≤ j < i 1 \le j < i 1j<i。只要a[j] < a[i],i位置元素跟在j元素后⾯就可以形成递增序列,⻓度为dp[j]+1
    因此,我们仅需找到满⾜要求的最⼤的dp[j] + 1即可。
    综上,dp[i] = max(dp[j] + 1, dp[i]) ,其中1 ≤ j < i && nums[j] < nums[i]
  1. 初始化:
    不⽤单独初始化,每次填表的时候,先把这个位置的数改成1 即可。
  2. 填表顺序:
    显⽽易⻅,填表顺序「从左往右」
#include <bits/stdc++.h>
using namespace std;

const int N = 5010;

int n;
int a[N];
int f[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    
    int ret = 1;
    for (int i = 1; i <= n; i++)
    {
        f[i] = 1;

        for (int j = 1; j < i; j++)
        {
            if (a[j] < a[i])
            {
                f[i] = max(f[i], f[j] + 1);
            }
        }
        ret = max(ret, f[i]);
    }
    cout << ret << endl;
    
    return 0;
}
最长上升子序列2

利⽤贪⼼+⼆分优化动态规划:

  • 我们在考虑最⻓递增⼦序列的⻓度的时候,其实并不关⼼这个序列⻓什么样⼦,我们只是关⼼最后⼀个元素是谁。这样新来⼀个元素之后,我们就可以判断是否可以拼接到它的后⾯。
  • 因此,我们可以创建⼀个数组,统计⻓度为 x 的递增⼦序列中,最后⼀个元素是谁。为了尽可能的让这个序列更⻓,我们仅需统计⻓度为 x 的所有递增序列中最后⼀个元素的「最⼩值」。
  • 统计的过程中发现,数组中的数呈现「递增」趋势,因此可以使⽤「⼆分」来查找插⼊位置
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;

int n;
int a[N];
int f[N], len;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    
    for (int i = 1; i <= n; i++)
    {
        if (len == 0 || a[i] > f[len]) f[++len] = a[i];
        else
        {
            //二分插入位置
            int l = 1, r = len;
            while (l < r)
            {
                int mid = (l + r) / 2;
                if (f[mid] >= a[i]) r = mid;
                else l = mid + 1;
            }
            f[l] = a[i];
        }
    }
    cout << len << endl;
    
    return 0;
}
P1091 [NOIP 2004 提高组] 合唱队形 - 洛谷

对于每⼀个位置i ,计算:

  • 从左往右看:以i 为结尾的最⻓上升⼦序列f[i]
  • 从右往左看:以i 为结尾的最⻓上升⼦序列g[i]
    最终结果就是所有f[i] + g[i] - 1⾥⾯的最⼤值
#include <bits/stdc++.h>
using namespace std;

const int N = 110;

int n;
int a[N];
int f[N], g[N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    
    // 从左往右
    for(int i = 1; i <= n; i++)
    {
        f[i] = 1;
        for(int j = 1; j < i; j++)
        {
            if(a[j] < a[i])
            {
                f[i] = max(f[i], f[j] + 1);
            }
        }
    }
    
    // 从右往左
    for(int i = n; i >= 1; i--)
    {
        g[i] = 1;
        for(int j = n; j > i; j--)
        {
            if(a[j] < a[i])
            {
                g[i] = max(g[i], g[j] + 1);
            }
        }
    }
    
    int ret = 0;
    for(int i = 1; i <= n; i++)
    {
        ret = max(ret, f[i] + g[i] - 1);
    }
    
    cout << n - ret << endl;
    
    return 0;
}
牛可乐和最长公共子序列
  1. 状态表⽰:
    dp[i][j]表⽰:s1的[1,i]区间以及s2的[1,j]区间内的所有的⼦序列中,最⻓公共⼦序列的
    ⻓度。
    那么dp[n][m]就是我们要的结果。
  2. 状态转移⽅程:
    对于dp[i][j] ,我们可以根据s1[i]s2[j]的字符分情况讨论:
    a. 两个字符相同s1[i] = s2[j]:那么最⻓公共⼦序列就在s1的[1, i - 1]以及s2的[1, j - 1]区间上找到⼀个最⻓的,然后再加上s1[i]即可。因此dp[i][j] = dp[i - 1][j - 1] + 1
    b. 两个字符不同s1[i] != s2[j]:那么最⻓公共⼦序列⼀定不会同时以s1[i]s2[j]结尾。那么我们找最⻓公共⼦序列时,有下⾯三种策略:
  • 去s1 的[1, i - 1]以及s2的[1, j]区间内找:此时最⼤⻓度为dp[i - 1][j]
  • 去s1 的[1, i]以及s2 的[1, j - 1]区间内找:此时最⼤⻓度为dp[i][j - 1]
  • 去s1 的[1, i - 1]以及s2 的[1, j - 1]区间内找:此时最⼤⻓度为dp[i - 1][j - 1]
    我们要三者的最⼤值即可。但是我们仔细观察会发现,第三种包含在第⼀种和第⼆种情况⾥⾯,但是我们求的是最⼤值,并不影响最终结果。因此只需求前两种情况下的最⼤值即可。
    综上,状态转移⽅程为:
    if(s1[i] = s2[j]) dp[i][j] = dp[i - 1][j - 1] + 1 ;
    if(s1[i] != s2[j]) dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
  1. 初始化:
    直接填表即可。
  2. 填表顺序:
    根据「状态转移⽅程」得:从上往下填写每⼀⾏,每⼀⾏从左往右
#include <bits/stdc++.h>
using namespace std;

const int N = 5010;

string s, t;
int f[N][N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    while (cin >> s >> t)
    {
        int n = s.size(), m = t.size();
        
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
            {
                if (s[i - 1] == t[j - 1]) 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;
}
P2758 编辑距离 - 洛谷

两个字符串之间的dp 问题,与最⻓公共⼦序列的分析⽅式类似。

  1. 状态表⽰:
    dp[i][j] 表⽰:字符串A 中[1, i] 区间与字符串B 中[1, j] 区间内的编辑距离。
    那么dp[n][m] 就是我们要的结果
  2. 状态转移⽅程:
    对于dp[i][j] ,我们可以根据A[i]B[j] 的字符分情况讨论:
    a. 两个字符相同A[i] = B[j] :那么dp[i][j]就是A的[1, i - 1]以及B的[1, j - 1]区间内编辑距离dp[i][j] = dp[i - 1][j - 1],因此;
    b. 两个字符不同A[i] != B[j] :那么对于A 字符串,我们可以进⾏下⾯三种操作:
  • 删掉A[i]:此时dp[i][j]就是A的[1, i - 1]以及B的[1, j]区间内的编辑距离,因此dp[i][j] = dp[i - 1][j] + 1
  • 插⼊⼀个字符:在字符串A的后⾯插⼊⼀个B[j],此时的dp[i][j]就是A的[1, i]以及B的[1, j - 1]区间内的编辑距离,因此dp[i][j] = dp[i][j - 1] + 1
  • A[i]替换成B[j]:此时的dp[i][j]就是A的[1, i - 1]以及B的[1, j - 1]区间内的编辑距离,因此dp[i][j] = dp[i - 1][j - 1] + 1
    我们要三者的最⼩值即可。
  1. 初始化:
    需要注意,当i,j等于0的时候,这些状态也是有意义的。我们可以全部删除,或者全部插⼊让
    两者相同。
    因此需要初始化第⼀⾏dp[0][j] = j (1 ≤ j ≤ m) ,第⼀列dp[i][0] = i (1 ≤ i ≤ n)
  2. 填表顺序:
    初始化完之后,从[1, 1] 位置开始从上往下每⼀⾏,每⼀⾏从左往右填表即可
#include <bits/stdc++.h>
using namespace std;

const int N = 2010;

string a, b;
int n, m;
int f[N][N];

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> a >> b;
    n = a.size(); m = b.size();
    a = " " + a; b = " " + b;

    //初始化
    for (int i = 1; i <= n; i++) f[i][0] = i;
    for (int j = 1; j <= m; j++) f[0][j] = j;

    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= m; j++)
        {
            if (a[i] == b[j]) f[i][j] = f[i-1][j-1];
            else f[i][j] = min(min(f[i-1][j], f[i-1][j-1]), f[i][j-1]) + 1;
        }
    }
    cout << f[n][m] << endl;
    
    return 0;
}

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

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

相关文章

LangChain4j(1):初步认识Java 集成 LLM 的技术架构

LangChain 作为构建具备 LLM 能力应用的框架&#xff0c;虽在 Python 领域大放异彩&#xff0c;但 Java 开发者却只能望洋兴叹。LangChain4j 正是为解决这一困境而诞生&#xff0c;它旨在借助 LLM 的强大效能&#xff0c;增强 Java 应用&#xff0c;简化 LLM 功能在Java应用中的…

【C++算法】53.链表_重排链表

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a; 题目链接&#xff1a; 143. 重排链表 题目描述&#xff1a; 解法 模拟 找到链表的中间节点 快慢双指针 把后面的部分逆序 双指针&#xff0c;三指针&#xff0c;头插法 合并两个链表 合并两个有…

多卡分布式训练:torchrun --nproc_per_node=5

多卡分布式训练:torchrun --nproc_per_node=5 1. torchrun 实现规则 torchrun 是 PyTorch 提供的用于启动分布式训练作业的实用工具,它基于 torch.distributed 包,核心目标是简化多进程分布式训练的启动和管理。以下是其主要实现规则: 进程启动 多进程创建:torchrun 会…

Elasticsearch:加快 HNSW 图的合并速度

作者&#xff1a;来自 Elastic Thomas Veasey 及 Mayya Sharipova 过去&#xff0c;我们曾讨论过搜索多个 HNSW 图时所面临的一些挑战&#xff0c;以及我们是如何缓解这些问题的。当时&#xff0c;我们也提到了一些计划中的改进措施。本文正是这项工作的成果汇总。 你可能会问…

图片中文字无法正确显示的解决方案

图片中文字无法正确显示的解决方案 问题描述 在 Linux 系统中生成图片时&#xff0c;图片中的文字&#xff08;如中文&#xff09;未能正确显示&#xff0c;可能表现为乱码或空白。这通常是由于系统缺少对应的字体文件&#xff08;如宋体/SimSun&#xff09;&#xff0c;或者…

ISP--Demosaicking

文章目录 前言算法解释简单的线性插值代码实现 色差法和色比法基于方向加权的方法RB缺失的G通道的插值RB缺失的BR的插值G缺失的BR的插值代码实现 基于边缘检测的方法计算缺失的G计算缺失的RB值/计算缺失的G值 前言 人眼之所以有能感受到自然界的颜色&#xff0c;是因为人眼的感…

国标GB28181协议EasyCVR视频融合平台:5G时代远程监控赋能通信基站安全管理

一、背景介绍 随着移动通信行业的迅速发展&#xff0c;无人值守的通信基站建设规模不断扩大。这些基站大多建于偏远地区&#xff0c;周边人迹罕至、交通不便&#xff0c;给日常的维护带来了极大挑战。其中&#xff0c;位于空旷地带的基站设备&#xff0c;如空调、蓄电池等&…

模拟-与-现实协同训练:基于视觉机器人操控的简单方法

25年3月来自 UT Austin、Nvidia、UC Berkeley 和纽约大学的论文“Sim-and-Real Co-Training: A Simple Recipe for Vision-Based Robotic Manipulation”。 大型现实世界机器人数据集在训练通才机器人模型方面拥有巨大潜力&#xff0c;但扩展现实世界人类数据收集既耗时又耗资…

WRS-PHM电机智能安康系统:为浙江某橡胶厂构筑坚实的生产防线

以行业工况为背景 一、顾客工厂的背景 浙江某橡胶厂以电机为中心生产设备必须连续平稳运行。但由于缺乏有效的故障预警体系&#xff0c;电机故障就像潜伏着的“不定时炸弹”,不但不时地造成生产流程的中断&#xff0c;也使对生产进行管理异常艰难&#xff0c;对持续安全生产提…

将 CrewAI 与 Elasticsearch 结合使用

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何使用 CrewAI 为你的代理团队创建一个 Elasticsearch 代理&#xff0c;并执行市场调研任务。 CrewAI 是一个用于编排代理的框架&#xff0c;它通过角色扮演的方式让多个代理协同完成复杂任务。 如果你想了解更多关于代理…

Spring 的 IoC 和 DI 详解:从零开始理解与实践

Spring 的 IoC和 DI 详解&#xff1a;从零开始理解与实践 一、IoC&#xff08;控制反转&#xff09; 1、什么是 IoC&#xff1f; IoC 是一种设计思想&#xff0c;它的核心是将对象的创建和管理权从开发者手中转移到外部容器&#xff08;如 Spring 容器&#xff09;。通过这种…

ZYNQ笔记(四):AXI GPIO

版本&#xff1a;Vivado2020.2&#xff08;Vitis&#xff09; 任务&#xff1a;使用 AXI GPIO IP 核实现按键 KEY 控制 LED 亮灭&#xff08;两个都在PL端&#xff09; 一、介绍 AXI GPIO (Advanced eXtensible Interface General Purpose Input/Output) 是 Xilinx 提供的一个可…

实操(环境变量)Linux

环境变量概念 我们用语言写的文件编好后变成了程序&#xff0c;./ 运行的时候他就会变成一个进程被操作系统调度并运行&#xff0c;运行完毕进程相关资源被释放&#xff0c;因为它是一个bash的子进程&#xff0c;所以它退出之后进入僵尸状态&#xff0c;bash回收他的退出结果&…

Word / WPS 页面顶部标题 段前间距 失效 / 不起作用 / 不显示,标题紧贴页眉 问题及解决

问题描述&#xff1a; 在 Word 或者 WPS 里面&#xff0c;如果不是新的一节&#xff0c;而是位于新的一页首行时&#xff0c;不管怎么设置段前间距&#xff0c;始终是失效的&#xff0c;实际段前间距一直是零。 解决方案&#xff1a; 查询了很多方案均无法解决问题&#xff…

Linux自行实现的一个Shell(15)

文章目录 前言一、头文件和全局变量头文件全局变量 二、辅助函数获取用户名获取主机名获取当前工作目录获取最后一级目录名生成命令行提示符打印命令行提示符 三、命令处理获取用户输入解析命令行执行外部命令 四、内建命令添加环境变量检查和执行内建命令 五、初始化初始化环境…

在 Q3D 中提取汇流条电感

汇流条排简介和设计注意事项 汇流条排是用于配电的金属导体&#xff0c;在许多应用中与传统布线相比具有设计优势。在设计母线排时&#xff0c;必须考虑几个重要的因素&#xff1a; 低电感&#xff1a;高频开关内容会导致无功损耗&#xff0c;从而降低效率电容&#xff1a;管…

MySQL:事务的理解

一、CURD不加控制&#xff0c;会有什么问题 &#xff08;1&#xff09;因为&#xff0c;MySQL里面存的是数据&#xff0c;所以很有可能会被多个客户访问&#xff0c;所以mysqld可能一次会接受到多个关于CURD的请求。&#xff08;2&#xff09;且mysql内部是采用多线程来完成数…

python 基础:句子缩写

n int(input()) for _ in range(n):words input().split()result ""for word in words:result word[0].upper()print(result)知识点讲解 input()函数 用于从标准输入&#xff08;通常是键盘&#xff09;读取用户输入的内容。它返回的是字符串类型。例如在代码中…

Ruoyi-vue plus 5.2.2 flowble 结束节点异常错误

因业务要求&#xff0c; 我在结束节点的结束事件中&#xff0c;制作了一个归档的事件&#xff0c;来执行一个业务。 始终都会报错&#xff0c; 错误信息 ${archivTemplateListener} did not resolve to an implementation of interface org.flowable.engine.delegate.Execution…

Sublime Text使用教程(用Sublime Text编写C语言程序)

Sublime Text 是一款当下非常流行的文本编辑器&#xff0c;其功能强大&#xff08;提供有众多的插件&#xff09;、界面简洁、还支持跨平台使用&#xff08;包括 Mac OS X、Linux 和 Windows&#xff09;。 在程序员眼中&#xff0c;Sublime Text 不仅仅是一个文本编辑器&…