最长上升子序列问题也叫做LIS(LIS : longest increasing sequence)
最长上升子序列,顾名思义就是找到一串数组中,严格单调上升的子序列最大长度。
原题链接
(代码粘贴即可通过)
首先举个例子:
假如给定数组: [3 1 2 1 8 5 6] ,问题是找出这组数的最长上升子序列的长度。
很显然,使用暴力是很难去解决这道问题的,所以就使用到了动态规划。
对于这道题目,我们需要一个dp数组
,来记录上升子序列的长度。
dp[i]
表示以第i
个数结尾的上升子序列的长度,那么我们最终要求的结果就是dp数组
中的最大值,也就是对dp数组
从头遍历到尾,看看以哪个数结尾的上升子序列长度最大,就输出结果。
状态转移方程为:f[i] = max(f[i] , f[j] + 1) {条件:如果i上的数严格大于j上的数的话}
那么对于上述这道题,我们来写一下代码。
input :
7
3 1 2 1 8 5 6
output :
输出最长上升子序列长度
#include <iostream>
using namespace std;
int main()
{
int n;
cin >> n; //接收输入样例中数组的个数
int f[n]; //定义动态规划数组
int w[n]; //接收数组
for(int i = 0; i < n; i++) cin >> w[i];
// 进行状态转移
int res = 0; // 使用res来记录最长上升子序列的长度。
for(int i = 0; i < n; i++)
{
f[i] = 1; //先对f[i]进行复制,因为每个数都是 以自己结尾的长度为1的上升子序列
for(int j = 0; j < i; j++)
{
if(w[i] > w[j]) // 判断如果i的数严格大于j上的数,才进行状态转移
{
f[i] = max(f[i] , f[j] + 1);
}
}
res = max(res , f[i]);
}
cout << res << endl;
return 0;
}
上面这道题目也就是最简单的上升子序列问题了,那么我们再来看一下进阶的最长上升子序列问题。
原题通道~
题目简介:
假如给我们一组数,这组数代表了一组山的海拔高度,一个人只可以从左往右走,可以上山,也可以下山,但是下山之后就不可以继续上山(也就是说一旦下山,那么接下来走的每一座山的海拔高度都必须逐渐降低),那么我们要求这个人最多可以走多少座山。
input:
8
186 186 150 200 160 130 197 220
output:
输出最多可以走多少座山。
这道题目的意思,也很容易懂,总共有两个条件:
(1)这个人只可以从左往右走
(2)这个人可以上山,也可以下山,但是下山之后,就不可以再向海拔高的山走了
那么由于这个人可以上山,也可以下山,但是下山之后,只能继续下山。
所以我们只需要知道每个点左边的最长上升子序列(f[i])表示
和右边的最长下降子序列(f[i]表示)
即可,那么对于右边的最长下降子序列又可以转化为从右往左的最长上升子序列
,所以就变为了求每个点的两边的最长上升子序列,以这个点转折,可以最多走过的山的数目为:f[i] + g[i] - 1
(减去1是f
和g
数组各加了一次重复了,所以减去)
那么接下来我们来看一下代码:(复制提交即可通过)
#include <iostream>
using namespace std;
int n;
int main()
{
cin >> n;
int w[n]; // 用来接收数组
int f[n]; // 表示某个点左边的 从左向右的最长上升子序列
int g[n]; // 表示某个点右边的 从右向左的最长上升子序列
for(int i = 0; i < n; i++) cin >> w[i]; // 读入数组
// 求每个点左边的最长上升子序列
for(int i = 0; i < n; i++)
{
f[i] = 1; //同样初始化 f[i] ,以当前经典转折,至少可以登上当前这座山
for(int j = 0; j < i; j++)
{
if(w[i] > w[j])
{
f[i] = max(f[i] , f[j] + 1);
}
}
}
// 求每个点右边的从右向左的最长上升子序列
for(int i = n - 1; i >= 0; i--)
{
g[i] = 1;
for(int j = n-1; j > i; j--)
{
if(w[i] > w[j])
{
g[i] = max(g[i] , g[j] + 1);
}
}
}
int res = 0; // 记录答案
for(int i = 0; i < n; i++) res = max(res , f[i] + g[i] - 1);
cout << res << endl;
return 0;
}
最长上升子序列问题(二)将会讲到如何利用最长上升子序列和dfs和贪心
来解决问题。