
💡Yupureki:个人主页
✨个人专栏:《C++》 《算法》《Linux系统编程》《高并发内存池》《MySQL数据库》
🌸Yupureki🌸的简介:

目录
[1. 最长上升子序列](#1. 最长上升子序列)
[2. 合唱队形](#2. 合唱队形)
[3. 最长公共子序列](#3. 最长公共子序列)
[4. 编辑距离](#4. 编辑距离)
1. 最长上升子序列
题目链接:

算法原理
- 状态方程:dp[i]表示以下标为i元素为结尾的最长子序列。最终结果是整张表中的最大值
- 状态转移:既然是i元素为结尾,因此我们需要与前面的子序列做连接。
- 自己跟自己玩,长度为1
- 跟前面的所有子序列 做连接,前提是第i个元素的大小必须大于子序列的末尾元素。
- 初始化:不用单独初始化,每次填表的时候,先把这个位置的数改成1即可。
- 填表顺序:从左到右
代码示例
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. 合唱队形
题目链接:

算法原理
- 状态表示:两张dp[i]表。
- dp1[i]表示以第i个元素为结尾,从左到右的最长子序列的长度
- dp2[i]表示以第i个元素为结尾,从右到左的最长子序列的长度
- 最终整个以中间元素为分割线,从左到右,从右到左都呈递增的序列长度即为len = max(dp1[i] + dp2[i]),要分出去的学生个数即为n - len
- 状态转移:分别从左向右,从右向左打两张表即可
- 初始化:不需要做额外的初始化
- 填表顺序:分别从左向右,从右向左
代码示例
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. 最长公共子序列
题目链接:

算法原理
- 状态表示:dp[i][j]表示第一个子序列前i个元素中,第二个子序列前j个元素中,最长的公共子序列长度
- 状态转移:
- 如果s1[i] == s2[j],说明可以以这个字母做结尾,形成公共子序列。那么只需要找到第一个序列前i - 1,第二个序列前j - 1中找到最长的公共子序列拼接上去即可。即dp[i][j] = dp[i - 1][j - 1] + 1;
- 如果s1[i] != s2[i],说明最长公共子序列不可能同时以s1[i]和s2[j]做结尾,那么有两种策略
- 在s1的前i - 1,s2的前j个中找
- 在s1的前i ,s2的的前j - 1个中找
- 最终结果取二者最大值dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
- 初始化:不需要做额外的初始化
- 填表顺序:从左到右
代码示例
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. 编辑距离
题目链接:

算法原理
- 状态表示:dp[i][j]表示考虑a的前i个元素,b的前j个元素,将a转换成b的最少操作次数
- 状态转移:
- 当a[i] == b[j]时,直接考虑dp[i-1][j-1]即可
- 当a[i] != b[j]时
- 删除a[i]:相当于dp[i-1][j]
- 插入一个字符(b[j]):相当于dp[i][j - 1]
- 将a[i]改为b[j]:相当于dp[i-1][j-1]
- 取这三者的最小值,加1即可
- 初始化:不需要做额外的初始化
- 填表顺序:从左到右
代码示例
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;
}