《算法竞赛从入门到国奖》算法基础:动态规划-最长子序列

💡Yupureki:个人主页

✨个人专栏:《C++》 《算法》《Linux系统编程》《高并发内存池》《MySQL数据库》

《个人在线OJ平台》


🌸Yupureki🌸的简介:


目录

[1. 最长上升子序列](#1. 最长上升子序列)

算法原理

代码示例

[2. 合唱队形](#2. 合唱队形)

算法原理

代码示例

[3. 最长公共子序列](#3. 最长公共子序列)

算法原理

代码示例

[4. 编辑距离](#4. 编辑距离)

算法原理

代码示例


1. 最长上升子序列

题目链接:

B3637 最长上升子序列 - 洛谷

算法原理

  1. 状态方程:dp[i]表示以下标为i元素为结尾的最长子序列。最终结果是整张表中的最大值
  2. 状态转移:既然是i元素为结尾,因此我们需要与前面的子序列做连接。
    1. 自己跟自己玩,长度为1
    2. 跟前面的所有子序列 做连接,前提是第i个元素的大小必须大于子序列的末尾元素。
  3. 初始化:不用单独初始化,每次填表的时候,先把这个位置的数改成1即可。
  4. 填表顺序:从左到右

代码示例

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    int n; cin >> n;
    vector<int> v(n);
    vector<int> dp(n);
    for (int i = 0; i < n; i++)
    {
        int num; cin >> num;
        v[i] = num;
    }
    int ret = 0;
    for(int i = 0;i<n;i++)
    {
        dp[i] = 1;
        for(int j = i - 1;j>=0;j--)
        {
            if(v[i] > v[j])
            {
                dp[i] = max(dp[i],dp[j] + 1);
                ret = max(ret,dp[i]);
            }
        }
    }
    cout << ret;
    return 0;
}

2. 合唱队形

题目链接:

P1091 [NOIP 2004 提高组] 合唱队形 - 洛谷

算法原理

  1. 状态表示:两张dp[i]表。
    1. dp1[i]表示以第i个元素为结尾,从左到右的最长子序列的长度
    2. dp2[i]表示以第i个元素为结尾,从右到左的最长子序列的长度
    3. 最终整个以中间元素为分割线,从左到右,从右到左都呈递增的序列长度即为len = max(dp1[i] + dp2[i]),要分出去的学生个数即为n - len
  2. 状态转移:分别从左向右,从右向左打两张表即可
  3. 初始化:不需要做额外的初始化
  4. 填表顺序:分别从左向右,从右向左

代码示例

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    int n;cin>>n;
    vector<int> v;
    vector<int> dp1(n,1);
    vector<int> dp2(n,1);
    for(int i = 0;i<n;i++)
    {
        int num;cin>>num;
        v.push_back(num);
    }
    for(int i = 0;i<n;i++)
    {
        for(int j = 0;j<i;j++)
        {
            if(v[j] < v[i])
                dp1[i] = max(dp1[i],dp1[j]+1);
        }
    }
    for(int i = n-1;i>=0;i--)
    {
        for(int j = n-1;j>i;j--)
        {
            if(v[j] < v[i])
                dp2[i] = max(dp2[i],dp2[j]+1);
        }
    }
    int ret = 0;
    for(int i = 0;i<n;i++)
    {
        ret = max(ret,dp1[i] + dp2[i] - 1);
    }
    cout<<n-ret;
    return 0;
}

3. 最长公共子序列

题目链接:

牛可乐和最长公共子序列

算法原理

  1. 状态表示:dp[i][j]表示第一个子序列前i个元素中,第二个子序列前j个元素中,最长的公共子序列长度
  2. 状态转移:
    1. 如果s1[i] == s2[j],说明可以以这个字母做结尾,形成公共子序列。那么只需要找到第一个序列前i - 1,第二个序列前j - 1中找到最长的公共子序列拼接上去即可。即dp[i][j] = dp[i - 1][j - 1] + 1;
    2. 如果s1[i] != s2[i],说明最长公共子序列不可能同时以s1[i]和s2[j]做结尾,那么有两种策略
      1. 在s1的前i - 1,s2的前j个中找
      2. 在s1的前i ,s2的的前j - 1个中找
      3. 最终结果取二者最大值dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
  3. 初始化:不需要做额外的初始化
  4. 填表顺序:从左到右

代码示例

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    string a, b;
    while (cin >> a >> b)
    {
        vector<vector<int>> dp(a.size() + 1, vector<int>(b.size() + 1));
        for (int i = 1; i <= a.size(); i++)
        {
            for (int j = 1; j <= b.size(); j++)
            {
                if (a[i - 1] == b[j - 1])
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                else
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        cout << dp[a.size()][b.size()] << endl;
    }
}

4. 编辑距离

题目链接:

P2758 编辑距离 - 洛谷

算法原理

  1. 状态表示:dp[i][j]表示考虑a的前i个元素,b的前j个元素,将a转换成b的最少操作次数
  2. 状态转移:
    1. 当a[i] == b[j]时,直接考虑dp[i-1][j-1]即可
    2. 当a[i] != b[j]时
      1. 删除a[i]:相当于dp[i-1][j]
      2. 插入一个字符(b[j]):相当于dp[i][j - 1]
      3. 将a[i]改为b[j]:相当于dp[i-1][j-1]
      4. 取这三者的最小值,加1即可
  3. 初始化:不需要做额外的初始化
  4. 填表顺序:从左到右

代码示例

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
using namespace std;

int main()
{
    string a,b;cin>>a>>b;
    int n = a.size();
    int m = b.size();
    vector<vector<int>> dp(n+1,vector<int>(m+1));
    for (int i = 0; i <= n; i++) {
        dp[i][0] = i;
    }
    for (int j = 0; j <= m; j++) {
        dp[0][j] = j;
    }
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=m;j++)
        {
            if(a[i-1] == b[j-1])
                dp[i][j] = dp[i-1][j-1];
            else
                dp[i][j] = min(dp[i][j-1],min(dp[i-1][j],dp[i-1][j-1])) +1;
        }
    }
    cout<<dp[n][m];
    return 0;
}
相关推荐
淡海水6 分钟前
ComfyUI全面掌握-知识点详解——基础示例:文生图与图生图实操(参数+案例)
大数据·人工智能·算法·comfyui
05候补工程师10 分钟前
【硬核干货】用“算法”思维袭英语新题型:集合逆清晰除与降维打击解题法
经验分享·笔记·考研·算法·学习方法
刀法如飞19 分钟前
Palantir Ontology 数据结构分析,与ER/OOP/DDD有什么区别?
人工智能·算法·架构
白藏y19 分钟前
【数据结构】简单选择排序
数据结构·算法·排序算法
信奥胡老师24 分钟前
B3930 [GESP202312 五级] 烹饪问题
开发语言·数据结构·c++·学习·算法
许长安28 分钟前
Redis 跳表实现详解
数据库·c++·经验分享·redis·笔记·缓存
paeamecium39 分钟前
【PAT甲级真题】- Shortest Distance (20)
数据结构·c++·算法·pat考试·pat
aisifang0043 分钟前
GPT-Image2去偏见技术新突破
人工智能·算法·机器学习
吴可可1231 小时前
点在线上判定与多段线分割
算法·c#
REDcker1 小时前
C++循环与编译器优化详解 别名不变量向量化与GCC Clang验证及perf实践
java·jvm·c++·c·clang·gcc