最长递增子序列-要求输出序列-动态规划-C++

问题

最长递增子序列

•子序列:是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

•给你一个整数数组 nums ,找到其中最长严格递增子序列的长度,并输出该子序列。

•输入:nums = [10,9,2,5,3,7,101,18]

•输出:4

解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

测试用例:

•(1)输入:nums = [10,9,2,5,3,7,101,18]

•输出:子序列: [2,3,7,101],长度: 4 。

(2)输入:nums = [0,1,0,3,2,3]

输出:子序列: [0,1,2,3],长度: 4 。

(3)输入:nums = [2,2,2,2,2,2]

输出:子序列:[ 2],长度:1

请先在工程目录里建立input.txt文档,因为程序使用txt文档输入来接收数据

输入:

8
10 9 2 5 3 7 101 18

输出:

代码

#include<iostream>
#include<fstream>
#define maxsize 10000  //所容纳数组的最大长度
using namespace std;

int dp[maxsize];
int q = 10000;
int maxLen = 0;
int endIndex[maxsize] = { 0 };  记下该数字前一个较小的数的下标的数组
int nums[maxsize];
int index = 0;  //选择字典序最小的数字的下标


int main()
{
	void print(int index, int maxLength);
	int fun(int nums[], int size);
	int i;
	int size;
	ifstream fp_read("input.txt");
	ofstream fp_write("output.txt");
	fp_read >> size;
	for (i = 0; i < size; i++)
		fp_read>> nums[i];
	int lislength = fun(nums, size);
	fp_write << lislength << endl;
	cout << "最长递增子序列长度为: " << lislength << endl;
	cout << "最长递增子序列为: ";   //先输出最长的长度,再输出递增子序列
	print(index, maxLen);
	cout << endl;
	return 0;
}

int fun(int nums[], int size)    //计算最长递增子序列长度
{
	int i, j;
	for (i = 0; i < size; i++)
	{
		dp[i] = 1; // 每个元素本身构成一个子序列,长度为1
		for (j = 0; j < i; j++)
			if (nums[j] < nums[i] && dp[i] < dp[j] + 1)
			{
				dp[i] = dp[j] + 1;
				endIndex[i] = j;  //记下该数字前一个较小的数的下标,
			}
		if (dp[i] > maxLen)
			maxLen = dp[i];
	}
	for (i = 0; i < size; i++)
		if (dp[i] == maxLen && nums[i] < q)
		{
			q = nums[i];
			index = i;
		}
	return maxLen;
}

void print(int index, int maxLength)   //递归打印最长递增子序列
{
	if (maxLength <= 0)
		return;
	print(endIndex[index], --maxLength); //根据最长递增子序列长度来决定递归次数,每次递归减一
	cout << nums[index] << ' ';
}

代码分析

1.递归方程为dp[i]=max(dp[j])+1,其中0j<inum[j]<num[i]

dp[i]这个数组代表以nums[i]结尾的数的子系列的长度。

关键代码如下:

for (i = 0; i < size; i++)
	{
		dp[i] = 1; // 每个元素本身构成一个子序列,长度为1
		for (j = 0; j < i; j++)
			if (nums[j] < nums[i] && dp[i] < dp[j] + 1)
			{
				dp[i] = dp[j] + 1;
				endIndex[i] = j;  //记下该数字前一个较小的数的下标,
			}
		if (dp[i] > maxLen)
			maxLen = dp[i];
	}

当nums[j]小于nums[i]的时候,还要继续判断当前dp[i]的值是否比dp[j]+1的值小,如果小于就更新dp[i]的值。这个递推具有最优子结构性质,从前往后地推,只要前面的数确定了它的最长子序列的长度,后面的数就能一一推出来。最终的结果就是将每一个数的dp[i]比较选出最大的那个dp[i]。

计算出最长的长度,还要将这个子序列输出,这里我添加一个数组index代表所标记的该数以该数结尾的数,它的前一个较小的数下标j记录为endIndex[i] = j。

2.

for (i = 0; i < size; i++)
		if (dp[i] == maxLen && nums[i] < q)
		{
			q = nums[i];
			index = i;
		}

对于上面的代码

由于当最长的系列递增的长度相同的时候,可能有多种情况,这里我只输出字典序最小的,只需判断所有dp[i]最大的值的数,它的nums[i]数值是最小的,记录它的下标index=i这个数即可。

3.

 void print(int index, int maxLength)   //递归打印最长递增子序列
{
	if (maxLength <= 0)
		return;
	print(endIndex[index], --maxLength); //根据最长递增子序列长度来决定递归次数,每次递归减一
	cout << nums[index] << ' ';
}

递归的打印这里,一开始函数中确定了最大的那个dp[i]所对应的数的下标,将它输入到index参数里面,然后再把最长的子序列的该长度maxlength输入到参数里面,然后进行定位输出,每次通过endIndex数组确定它前一个较小的数的下标。当maxLength小于等于0的时候就返回,这样就可以一次输出最终的子序列个数。

4.时间复杂度

对于输入大小为size的数组,存在两层嵌套for循环。外层循环用于遍历原始数组,内层循环用于比较前面的元素与当前元素的大小关系。

因此,该算法的时间复杂度为O(n^2)。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值