动态规划之 —— 最长上升子序列问题(一)

最长上升子序列问题也叫做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是fg数组各加了一次重复了,所以减去)

那么接下来我们来看一下代码:(复制提交即可通过)

#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和贪心来解决问题。

  • 8
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

11来了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值