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

💡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;
}
相关推荐
小樱花的樱花2 小时前
C++引用:高效编程的技巧
开发语言·数据结构·c++·算法
南境十里·墨染春水2 小时前
C++笔记 继承中重载规则 公有私有继承的区别(面向对象)
开发语言·c++·笔记
沉鱼.442 小时前
进制转换题
开发语言·c++·算法
罗湖老棍子2 小时前
维护序列(信息学奥赛一本通- P1551)(洛谷-P2023)
算法·线段树·区间修改区间查询·多重懒标记
Boop_wu2 小时前
[Java 算法] 归并排序
数据结构·算法·排序算法
liulilittle2 小时前
SQLITE3 KG-CC
数据库·c++·sqlite
今儿敲了吗2 小时前
49| 枚举排列
数据结构·c++·笔记·学习·算法
小李小李快乐不已2 小时前
docker(2)容器管理与镜像操作
运维·c++·docker·容器