动态规划问题之最长递增子序列

问题描述: 给定一个长度为N的数组,找出一个最长的递增子序列(给出的算法中只要满足<=即可,不需要严格递增)(不一定连续,但是顺序不能乱)。例如A{5,6,7,1,2,8},则其最长的单调递增子序列为{5,6,7,8},长度为4。

解题思路:
使用来两个数组longest和pre,longgest表示对应以当前元素为结尾的递增之序列长度,pre数组表示满足以当前元素为结尾的递增子序列的前一个元素。使用两层for循环,每次判断第i位的前面所有元素是否满足递增和判断是否longest[i]<longest[j]+1,满足则更新longest[i]元素值,同时更新pre[i],内层遍历完成后,记录当前当前递增子序列的最后一个数的索引和最长子序列的长度。

实现如下:

//获取最长递增子序列的长度
//p:给定的数组,length:给定数组的长度,pre:当前数的前驱,该前驱满足递增
//nIndex:最长子序列的最后一个数的下标
int LIS(int *p, int length, int *pre, int& nIndex) {
	int* longest = new int[length];
	int i, j;
	//初始时,以arr[i]结尾的最长子序列长度都为1,就是自己本身,前驱没有
	for (i = 0; i < length; i++)
	{
		longest[i] = 1;
		pre[i] = -1;
	}

	int nList = 1;
	nIndex = 0;
	//从第二个开始遍历,判断当前是否“递增”
	for ( i = 1; i < length; i++)
	{
		//遍历arr[i]前面的所有元素和arr[i]比较,比arr[j]大,则当前数对应的longest[i]就是arr[j]对应的longest[j]+1
		for ( j = 0; j < i; j++)
		{
			if (p[j]<=p[i])
			{
				if (longest[i]<longest[j]+1)//包含自身,所以+1
				{
					longest[i] = longest[j] + 1;
					pre[i] = j;//记录当前元素arr[i]的前驱,这里记录的是前驱元素的下标,前驱满足递增子序列的条件
				}
			}
		}
		//设置当前遍历到的最长递增子序列的长度和最长递增子序列的最后一个数的索引
		if (nList < longest[i])
		{
			nList = longest[i];
			nIndex = i;
		}
	}
	delete[] longest;//释放空间
	return nList;//返回最长递增子序列长度
}

如何还需要输出这个最长递增子序列,可以通过从最长递增子序列的最后一个和pre数组,找到那个最长递增子序列,因为pre数组储存的就是当前元素的前一个满足递增子序列的索引,最后再逆置输出即可。
实现如下:

void GetLis(int *arr, int *pre, int nIndex, vector<int>& lis) {
	//将最长递增子序列放入lis中,通过pre记录的下标来找那个最长递增子序列
	while (nIndex>=0)
	{
		lis.push_back(arr[nIndex]);
		nIndex = pre[nIndex];//(关键)索引和前驱的小标对应,和前面的pre[i]=j对应
	}
	cout << endl;
	//由于找的那个序列是从后往前开始中,所以最后还需要将集合中的顺序逆置
	reverse(lis.begin(), lis.end());
}

注意:以下这两中方法都不能求出那个最长递增子序列,这能求出其长度

//方法二:
//思路:遍历每次以i位置对应元素结尾的最长递增子序列
int getLIS1(int arr[], int size) {
	if (size<=0)
	{
		return 0;
	}
	int *dp = new int[size];
	dp[0] = 1;
	int maxLis = dp[0];
	for (int i = 1; i < size; i++)
	{
		dp[i] = 1;//排序都比前面的数小
		for (int j = 0; j < i; j++)
		{
			if (arr[i]>=arr[j] && dp[i] < dp[j]+1)
			{
				dp[i] = dp[j]+1;
			}
		}
		if (maxLis<dp[i])
		{
			maxLis = dp[i];
		}
	}
	return maxLis;
}

//方法三:使用类似于栈的思想实现,每次找小的
//注意这个站里面不是存的那个最长递增子序列,存的是个数,和最后一个是最长递增子序列的最后一个元素
int getLIS2(int arr[], int size) {
	if (size==0)
	{
		return 0;
	}
	vector<int> stack;
	stack.push_back(arr[0]);
	for (int i = 1; i < size; i++)
	{
		if (arr[i] > stack.back())//取vector的最后一个元素
		{
			stack.push_back(arr[i]);
		}
		else
		{
			//从前往后遍历,找出第一个大于arr[i]的元素,然后和他替换
			for (int j = 0; j < stack.size(); j++)
			{
				if (arr[j] >= arr[i]) 
				{
					arr[j] = arr[i];//替换
					break;
				}
			}
		}
	}
	return stack.size();
}

参考视频:https://www.bilibili.com/video/BV1et411T7SC?p=9
若有任何问题,欢迎读者随时批评指正,万分感谢!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值