一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第28天,点击查看活动详情。
题目
分析
- 子序列:从前往后挑,可以有间隔的挑选
- 状态表示f[i]:
- 集合:所有以第i个数结尾的上升子序列
- 属性:上升子序列长度的最大值Max
- 状态计算:
- 确定:第i个数是确定的
- 我们以第i - 1个数来分类
- 第一类:没有第i - 1 个数,也就是序列长度是1
- 第二类:倒数第二个数是原序列第一个数
- 第三类:倒数第二个数是原序列第二个数
- 第四类:倒数第二个数是原序列第三个数
- ……
- 最后一类:倒数第二个数是原序列第i - 1个数
- 时间复杂度:O(n^2)
代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n;
int a[N], f[N];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
{
f[i] = 1; // 最长上升子序列长度最少是1,只有a[i]这一个数
for (int j = 1; j < i; j++)
if (a[j] < a[i])
f[i] = max(f[i], f[j] + 1);
}
int res = 0;
for (int i = 1; i <= n; i++) res = max(res, f[i]);
printf("%d\n", res);
return 0;
}
怎样将最长序列保存下来
- 用g[N]数组来保存一下数据
g[i] = 0:表示只有一个数- 动态规划将最长子序列存下来,其实就是将转移存下来即可
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n;
int a[N], f[N], g[N];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
{
f[i] = 1; // 最长上升子序列长度最少是1,只有a[i]这一个数
g[i] = 0;
for (int j = 1; j < i; j++)
if (a[j] < a[i])
if (f[i] < f[j] + 1)
{
f[i] = f[j] + 1;
g[i] = j;
}
}
int k = 1;
for (int i = 1; i <= n; i++)
if (f[k] < f[i])
{
k = i;
}
printf("%d\n", f[k]);
// 倒着来输出
for (int i = 0, len = f[k]; i < len; i++)
{
printf("%d ", a[k]);
k = g[k];
}
printf("%d\n", res);
return 0;
}