【动态规划】最长上升子序列(Longest Increasing Subsequence)问题以及输出具体方案
最长上升子序列
两道模板题(一样的)
洛谷 B3637 最长上升子序列
AcWing 895. 最长上升子序列
题目描述
这是一个简单的动规板子题。
给出一个由 个不超过 的正整数组成的序列。请输出这个序列的最长上升子序列的长度。
最长上升子序列是指,从原序列中按顺序取出一些数字排在一起,这些数字是逐渐增大的。
输入格式
第一行,一个整数 ,表示序列长度。
第二行有 个整数,表示这个序列。
输出格式
一个整数表示答案。
样例 #1
样例输入 #1
6
1 2 4 1 3 4
样例输出 #1
4
提示
分别取出 、、、 即可。
标准模版代码
#include <iostream>
using namespace std;
const int N = 5010;//洛谷板子题是5000所以开大点
int n;
int f[N], a[N];
int main()
{
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &a[i]);
f[i] = 1;//初始子序列只有一个字母时长度为1
}
for (int i = 0; i < n; i++)
for (int j = 0; j < i; j++)
{
if (a[j] < a[i]) //上升
f[i] = max(f[i], f[j] + 1);//最长
}
int res = 0;
for (int i = 0; i < n; i++) res = max(res, f[i]);
printf("%d", res);
return 0;
}
二分+贪心优化
#include <iostream>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
int n;
int a[N];
vector<int> q;//因为二分后要根据下标修改元素,
//而双端队列deque不能根据下标修改元素,所以不能用deque
//而是用vector来模拟可根据下标修改元素的特殊队列
int main()
{
cin >> n;
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++)
{
if (q.empty() || a[i] > q.back()) q.push_back(a[i]);
else if (a[i] <= q.back())
{
int l = 0, r = q.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
if (q[mid] >= a[i]) r = mid;
else l = mid + 1;
}
q[r] = a[i];
}
}
cout << q.size();
return 0;
}
输出具体方案
代码解释1:int maxLen = *max_element(f.begin(), f.end());
// 代码整体功能:
// 这段代码的目的是在一个整数容器 f
中找到最大的元素,并将其存储在 maxLen
变量中。
// 代码解释:
// 首先,我们使用了标准库中的 max_element
函数。
// max_element
函数接受两个迭代器作为参数,这里是 f.begin()
和 f.end()
。
// f.begin()
表示容器 f
的起始迭代器,f.end()
表示容器 f
的结束迭代器。
// 这个函数会遍历 f
容器中的元素,从 f.begin()
开始,直到 f.end()
之前的元素。
// 然后,它会找出这些元素中的最大值。
// 接着,max_element
函数返回一个迭代器,该迭代器指向容器中最大元素的位置。
// 由于 max_element
函数返回的是一个迭代器,而我们想要的是元素的值,
// 所以在函数调用前使用 *
运算符进行解引用操作。
// 这将迭代器指向的元素的值提取出来,并存储在 maxLen
变量中。
代码解释2:int k = find(f.begin(), f.end(), maxLen) - f.begin();
// 代码整体功能:
// 这段代码的目的是在容器 f
中查找元素 maxLen
的位置,并将该位置存储在变量 k
中。
// 代码解释:
// 首先,使用 find
函数来查找元素。find
函数接受三个参数:
// 1. 起始迭代器 f.begin()
,表示从容器 f
的开始位置开始查找。
// 2. 结束迭代器 f.end()
,表示查找范围截止到容器 f
的末尾位置(不包括 f.end()
所指向的元素)。
// 3. 要查找的元素 maxLen
,它是之前代码中找出的容器 f
中的最大元素。
// find
函数会在 f.begin()
到 f.end()
的范围内查找第一个等于 maxLen
的元素。
// 如果找到了,find
函数会返回一个迭代器,该迭代器指向找到的元素。
// 如果没找到,find
函数会返回 f.end()
。
// 然后,通过 find(f.begin(), f.end(), maxLen) - f.begin()
计算元素 maxLen
在容器中的位置:
// 用 find
函数返回的迭代器减去 f.begin()
迭代器,得到的结果是一个整数,表示元素 maxLen
相对于容器 f
起始位置的偏移量。
// 这个偏移量存储在变量 k
中。
代码解释3:cout << path[i] << " \n"[i == path.size() - 1];
" \n"[i == path.size() - 1];
:这是一个比较巧妙的写法。" \n"是一个字符串常量,它包含一个空格和一个换行符。
[i == path.size() - 1]
是一个条件表达式,当i等于path.size() - 1(即遍历到最后一个元素)时,表达式的值为 1,此时取字符串" \n"中的第二个字符(即换行符\n);否则表达式的值为 0,取字符串" \n"中的第一个字符(即空格)。这样做的效果是,除了最后一个元素输出后换行,其他元素输出后都跟一个空格。
输出具体序列(DP做法,STL版本)
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int n = 0;
cin >> n;
vector<int> a(n, 0); // 原数组
vector<int> f(n, 1); // 状态
vector<int> g(n, 0); // 记录状态转移
for (int i = 0; i < n; i++) cin >> a[i];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < i; j++)
{
if (a[j] < a[i])
{
if (f[i] < f[j] + 1)
{
f[i] = f[j] + 1;
g[i] = j; // 记录i是由j更新的
}
}
}
}
int maxLen = *max_element(f.begin(), f.end());
int k = find(f.begin(), f.end(), maxLen) - f.begin();
vector<int> path; // 存具体方案
for (int i = 0; i < maxLen; i++)
{
path.push_back(a[k]);
k = g[k];
}
reverse(path.begin(), path.end());
for (int i = 0; i < path.size(); i++)
{
cout << path[i] << " \n"[i == path.size() - 1];
}
return 0;
}
输出具体序列(DP做法,数组版本)
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
const int N = 5010;
int n;
int a[N], f[N], g[N]; // 分别为原数组、状态、记录转态转移
int path[N]; // 记录具体方案
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
f[i] = 1;
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < i; j++)
{
if (a[j] < a[i])
{
if (f[i] < f[j] + 1)
{
f[i] = f[j] + 1;
g[i] = j; // 记录i是由j更新的
}
}
}
}
int maxlen = 0, maxindex;
for (int i = 0; i < n; i++)
{
if (maxlen < f[i])
{
maxlen = f[i]; // 记录最大长度
maxindex = i; // 记录最大值下标
}
}
for (int i = 0; i < maxlen; i++)
{
path[i] = a[maxindex];
maxindex = g[maxindex]; // 将当前下标更新为上一个下标
}
reverse(path, path + maxlen);
for (int i = 0; i < maxlen; i++)
{
cout << path[i] << " \n"[i == maxlen - 1];
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析