最长递增子序列(LIS)[通俗易懂]

最长递增子序列(LIS)[通俗易懂]①dp[i]表示以i为结尾的最长子序列长度②dp[i]表示长度为i的最长递增子序列末尾的数

大家好,又见面了,我是你们的朋友全栈君。

最长递增子序列(LIS)

问题描述:

求一个序列的最长递增子序列,这样的子序列是允许中间越过一些字符的,即留“空”。

例如:4 2 3 1 5 的最长递增子序列为 2 3 5,长度为 3 。

解法:

这里给出两种动态规划的做法,第二种是比较优化的 dp 。

① dp:dp[i] 表示以 i 结尾的最长递增子序列长度
在这里插入图片描述
第一个元素直接设置 LIS 长度为 1 即可。
在这里插入图片描述
处理第二个元素 2 的时候判断是否比前面的元素 4 大,没有的话那么以 2 为结尾的 LIS 就是 2,

即 LIS 长度为 1。
在这里插入图片描述
处理第三个元素 3 的时候需要跟前面的每个元素都进行比较,3 大于 2,则 LIS 的长度可能为 dp[1] + 1,

3 小于 4,则 LIS 的长度可能为 1,比较dp[1] + 1 和 1,取最大值,为 2 。
在这里插入图片描述
处理第四个元素 1,发现比前面的元素都小,那么以 1 为结尾的 LIS 只可能为 1,因此 LIS 的长度为 1。
在这里插入图片描述
处理最后一个元素 5,发现比前面的元素都大,那么以 5 结尾的 LIS 的长度可能为

dp[0] + 1,dp[1] + 1,dp[2] + 1,dp[3] + 1。

其中的最大值为 dp[2] + 1 = 3,因此 LIS 的长度为 3。

总结:

dp[i] 默认都为 1,因为以 i 结尾的 LIS 至少包含自己。

在 dp 表 0~i-1 中比对时,若 arr[i] > arr[j],

那么 dp[j] + 1 可能为 dp[i] 的最终值。

需要在所有的可能值中取最大值。

时间复杂度为 O(n2)。

② dp:dp[i] 表示长度为 i 的最长递增子序列(LIS)末尾的数
在这里插入图片描述
第一个元素直接加入 dp 表,dp[1] = 4,表示长度为 1 的 LIS 末尾的数当前为 4。
在这里插入图片描述
第二个元素为 2,因为 2 < 4,直接替换掉 4,dp[1] = 2 。

因为后面序列中的数字 > 2 的几率一定比 > 4 的几率高,有种贪心的感觉。
在这里插入图片描述
第三个元素为 3,由于 3 > dp[1] = 2,构成递增,dp[2] = 3,表示长度为 2 的 LIS 的末尾为 3 。
在这里插入图片描述
第四个元素为 1,由于 1 < dp[2] = 3,因此在前面一定有一个位置可以换成 1,

并且后面的递增性质不会被破坏。

第一个 > 1 的为 dp[1] = 2,因此将 dp[1] 置为 1。
在这里插入图片描述
最后一个元素为 5, 5 > dp[2] = 3,构成递归,故dp[3] = 5。

全部遍历完成,这个时候我们就可以发现 dp 数组的下标 3 就是我们要求的 LIS 长度。

参考代码:

// 这里的最长递增子序列是允许中间跨越其他子序列的 
#include<iostream>
#include<algorithm>
using namespace std;

int *arr;
int *dp;

// 经典问题 dp[i]的意思为以i为结尾的最长子序列为多少 
int getResult(int n)
{ 
   
	dp[0] = 1;
	for (int i = 1; i < n; i++)
	{ 
   
		int cnt = 1;
		for (int j = i - 1; j >= 0; j--)
		{ 
   
			if (arr[i] > arr[j])
			{ 
     // 保证递增 
				cnt = max(cnt, dp[j] + 1);
			}
		}
		dp[i] = cnt;
	}
	int ans = 0;
	for (int i = 0; i < n; i++)
	{ 
   
		ans = max(ans, dp[i]);
	}
	return ans;
}

// 二分查找变体 找到第一个大于等于n的位置index 
int BinarySearch(int *dp, int len, int n)
{ 
   
	int left = 1;
	int right = len;
	while (left < right)
	{ 
   
		int mid = (left + right) / 2;
		if (dp[mid] >= n)
		{ 
   
			right = mid;
		}
		else
		{ 
   
			left = mid+1;
		}
	}
	return right;
}

// 优化的dp dp数组的最终下标为答案 
int getResult1(int n)
{ 
   
	 dp[1] = arr[0];
	 int index = 1;
	 for (int i = 1; i < n; i++)
	 { 
   
	 	if (arr[i] > dp[index])
	 	{ 
   
	 		// 更新index 
	 		dp[++index] = arr[i];
		 }
		 else
		 { 
   
		 	// 把dp数组中第一个大于等于n的数字替换为arr[i] 
		 	int tempIndex = BinarySearch(dp, index, arr[i]);
		 	dp[tempIndex] = arr[i];
		 }
	 }
	 return index;
} 

int main(){ 
   
	int n;
	while (cin >> n)
	{ 
   
		arr = new int[n];
		dp = new int[n+1]; 
		for (int i = 0; i < n; i++)
		{ 
   
			cin >> arr[i];
		}
		int ans = getResult1(n);
		cout << ans << endl;
		delete[] arr;
		delete[] dp;
	}
	return 0;
} 

【END】感谢观看

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/130060.html原文链接:https://javaforall.net

(0)
上一篇 2022年4月27日 下午2:20
下一篇 2022年4月27日 下午3:20


相关推荐

  • 数字推盘游戏java_数字推盘游戏下载_数字推盘最强大脑app游戏下载最新版 v1.0-安族游戏网…

    数字推盘游戏java_数字推盘游戏下载_数字推盘最强大脑app游戏下载最新版 v1.0-安族游戏网…数字推盘游戏是一款让千万玩家们都为之沉迷的华容道数字解谜闯关类小游戏 在这个让人感受到真实互动的挑战模式之中 玩家们可以发挥自己的无限想象能力 空间推理能力以及你的聪明脑力开始你的数字挑战赛 简约清新的游戏界面设定 令人烧脑的数字解谜关卡 从现在开始 玩家们可以秀出你的手速去开始你的最高分创造吧 数字推盘游戏特色 1 游戏的画面设置的很简单 玩家们只需要挑战你的脑力与手速即可 2 但是操作起来不一

    2026年3月19日
    1
  • pycharm安装配置pep8插件(很简单)

    pycharm安装配置pep8插件(很简单)一 你得先安装一个 pycharm 二 安装 pep8 插件 在 pycharm 的 Terminal 上输入 pipinstallpe 点击回车 检查是否安装成功 setting gt projectinter 看右侧的 package 中是否有 pep8 三 打开 PyCharm File gt setting gt tools gt externalto

    2026年3月27日
    2
  • 搭建服务器上的GIT并实现自动同步到站点目录(www)「建议收藏」

    搭建服务器上的GIT并实现自动同步到站点目录(www)

    2022年2月11日
    76
  • Markdown中Latex 数学公式基本语法

    Markdown中Latex 数学公式基本语法Markdown中Latex数学公式基本语法公式排版分为两种排版:-行内公式:用\或者$包裹公式-独立公式:用\$包裹公式。例如:$\sum_{i=0}^{n}i^2$表示∑ni=0i2\sum_{i=0}^{n}i^2$$\sum_{i=0}^{n}i^2$$表示∑i=0ni2\sum_{i=0}^{n}i^2一下

    2022年6月16日
    40
  • mysql中有execute_jdbc连接mysql数据库

    mysql中有execute_jdbc连接mysql数据库最近在补基础知识,刚好补到C#中对数据库操作的一些技术,今天学习了ExecuteNonQuery的东西,看自己项目维护项目的代码和网上资料查询,基本上搞懂了ExecuteNonQuery的用法,小小的做个总结,供以后查阅。ExecuteNonQuery方法主要用来更新数据,当然也可以用来执行目标操作(例如查询数据库的结构或者创建诸如表等的数据库对象)。通常用它来执行insert、update、de…

    2025年10月30日
    4
  • navicat生成激活码时出错【最新永久激活】2022.02.21[通俗易懂]

    (navicat生成激活码时出错)好多小伙伴总是说激活码老是失效,太麻烦,关注/收藏全栈君太难教程,2021永久激活的方法等着你。IntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,下面是详细链接哦~https://javaforall.net/100143.htmlHCIQ56F36O-eyJsaWNlbnNlSWQi…

    2022年4月1日
    195

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号