当前位置: 首页 > news >正文

最长上升子序列


解题思路:

1.最长上升子序列的含义是从给定的数中选取尽量多的数字形成单调递增的序列

2.可以把以每一个数字形成单调结尾的方案数看待成一个子问题,然后对后面的子问题提供最优解

3.设定状态,dp【x】为以第x个位置上的数字结尾的序列长度

4.状态转移,如果以第x个位置上的数字结尾,前面a[j]<a[x]的,说明可以结尾,所以就是dp【x】=max(dp【x-1】+1);这里为什么是最大值呢?如果不行的话,则dp【x】=1;

5.初始化dp数组为1,因为本身就是一个单调递增的序列


#include<bits/stdc++.h>
using namespace std;
int a[10010],dp[10010];
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)//输入元素到数组中 
	cin>>a[i];
	
	for(int i=1;i<=n;i++)//初始化dp数组,最小值都为1 
	dp[i]=1;
	
	for(int i=2;i<=n;i++)//从第二项开始转移 
	{
		for(int j=1;j<=i;j++)//从他前面开始找 
		{
			if(a[i]>a[j])//如果可以a[i]结尾 
			{
				dp[i]=max(dp[j]+1,dp[i]);//状态转移方程 
			}
		}
	}
	
	for(int i=1;i<=n;i++)//输出方案数 
	cout<<dp[i]<<" ";
	
	return 0;
}

算法优化:

1.上面的思路本质上是在暴力枚举,把每个状态都比较一次,所以时间复杂度是O(n*n),在其中,有很多是没有必要去枚举的,导致浪费了更多的时间,当数据量较大的时候便超时

2.可以利用贪心的思想,如果设置f【i】为此时子序列长度为i的时候末尾的元素值,那么这个元素值较小的时候,才能为后面空出更多的空间

3.设置初始状态f【1】=a【1】,即第一个元素本身就是一个数位,从第二项开始,判断如果a[i]>f【len】的,说明子序列的长度又可以加1,先把a【i】放在新序列的末尾,长度len++,

 即f【++len】=a【i】;

4.当a【i】是小于f【len】的时候,说明他其实可以作为前面子序列中的末尾值,举个例子:

       原序列为:1 3 2 4 5,1作为上升子序列的第1项即f【1】=1;

                         3作为上升子序列的第二项f【2】=3

                         当到达2的时候,发现2比3小,那么此时的2完全可以取代3,因为2可以为后面腾                             出更多的空 间,所以f【2】=2

                        (因为这里只要求我们输出最长上升子序列的长度,所以不必考虑第二个位                                      置 到底是谁,只要确定目前有2个上升的序列长度即可!)

5.那么如何去查找呢?将此时更小的a【i】放到最合适的位置上,这里采用二分查找的方法(也就是真正降低时间复杂度的关键O(nlogn)),因为形成的f【len】已经值单调递增的序列,所以可以利用在排好序的序列中,将a【i】一直往前找,

举个例子:此时1,4,5已经是排好序的子序列,当前最长上升长度为3,也就是5的下标,遍历2的时候,发现比前面的值小,贪心思想来说,2更应该作为末尾值,所以往前找,直到找到比它刚刚小的值,然后放入到该位置后面即可

元素值14523
下标12345

6.最后输出f数组的长度即为最长上升子序列的长度

#include<bits/stdc++.h>
using namespace std;
const int MAXN=99999999;
int a[100010],f[100010];

int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		f[i]=MAXN; //设置最大值,方便后续替换 
	}
	
	f[1]=a[1];//以第一个元素作为子序列的第一个值 
	int len=1;//记录当前上升子序列的长度 
	
	for(int i=2;i<=n;i++)
	{
		int low=0,high=len,mid;//设置二分查找的区间 
		
		if(a[i]>f[len])//如果当前值是大于排好序的末尾元素值的 
		f[++len]=a[i];//加长子序列的长度,将该值作为该位置的末尾元素值
		else
		{
			
			while(low<high)//二分查找 当low>=high停止循环 
			{
				mid=(high+low)/2;
				
				if(f[mid]>a[i])//如果中间值大于a[i],继续往前找 
				high=mid;//上限更新 
				else//否则的话 
				low=mid+1;//下限更新 
			}
			f[low]=min(f[low],a[i]);//更新末尾数字 
		} 
	}
	cout<<len;
	return 0;
}

相关文章:

  • python毕业设计题目推荐飞机票销售订票系统
  • 如何恢复删除的数据(以损坏的U盘为例)
  • uverbs的交互方式——ioctl和write
  • Hadoop-Yarn
  • mysql面试题
  • Vue-条件渲染和循环渲染
  • 二、前端-VUE(2)
  • 在腾讯云服务器的Centos上从零开始部署并运行TinyWebServer服务器,过程记录(非常详细)
  • springboot系列(二十):如何通过redis实现手机号验证码功能 |超级详细,建议收藏
  • 原始套接字
  • 【C语言刷LeetCode】1953. 你可以工作的最大周数(M)
  • 猿创征文|Python基础——Visual Studio版本——Web开发
  • 【C语言刷LeetCode】395. 至少有 K 个重复字符的最长子串(M)
  • 利用 HomeAssistant 实现电脑远程开关机
  • 练习31-35:多表关联查询、多条件自连接查询、子查询、窗口函数等
  • (三)从jvm层面了解线程的启动和停止
  • 0x05 Python数据分析,Anaconda八斩刀
  • codis proxy处理流程
  • ES学习笔记(10)--ES6中的函数和数组补漏
  • Git同步原始仓库到Fork仓库中
  • javascript从右向左截取指定位数字符的3种方法
  • quasar-framework cnodejs社区
  • SQLServer之创建数据库快照
  • vue脚手架vue-cli
  • Vue组件定义
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 物联网链路协议
  • 一文看透浏览器架构
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • #etcd#安装时出错
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • (C#)一个最简单的链表类
  • (react踩过的坑)Antd Select(设置了labelInValue)在FormItem中initialValue的问题
  • (附源码)spring boot北京冬奥会志愿者报名系统 毕业设计 150947
  • (机器学习的矩阵)(向量、矩阵与多元线性回归)
  • (十)c52学习之旅-定时器实验
  • (实战)静默dbca安装创建数据库 --参数说明+举例
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • **PHP分步表单提交思路(分页表单提交)
  • **python多态
  • .NET6使用MiniExcel根据数据源横向导出头部标题及数据
  • .net对接阿里云CSB服务
  • .Net面试题4
  • .Net小白的大学四年,内含面经
  • .net中我喜欢的两种验证码
  • :not(:first-child)和:not(:last-child)的用法
  • @四年级家长,这条香港优才计划+华侨生联考捷径,一定要看!
  • [ 云计算 | AWS 实践 ] Java 如何重命名 Amazon S3 中的文件和文件夹
  • []我的函数库
  • [2009][note]构成理想导体超材料的有源THz欺骗表面等离子激元开关——
  • [2018/11/18] Java数据结构(2) 简单排序 冒泡排序 选择排序 插入排序
  • [2021]Zookeeper getAcl命令未授权访问漏洞概述与解决
  • [Android Pro] android 混淆文件project.properties和proguard-project.txt
  • [android] 天气app布局练习