最长上升子序列的两种解法

最长上升子序列的两种解法问题描述一个数的序列bi,当b1你的任务,就是对于给定的序列,求出最长上升子序列的长度。动态规划法如何把这个问题分解成子问题呢?经过分析,发现“求以ak(k=1,2,3…N)为终点的最长上升子序列的长度”是个好的子问题――这里把一个上升子序列中最右边的那个数,称为该子序列的“终点”。虽然这个子问题和原问题形式上并不完全一样,但是只要这N个子问题都解决了,那么这N

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

问题描述

一个数的序列bi,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们可以得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK <= N。比如,对于序列(1, 7, 3, 5, 9, 4, 8),有它的一些上升子序列,如(1, 7), (3, 4, 8)等等。这些子序列中最长的长度是4,比如子序列(1, 3, 5, 8).

你的任务,就是对于给定的序列,求出最长上升子序列的长度。

动态规划法

如何把这个问题分解成子问题呢?经过分析,发现 “求以ak(k=1, 2, 3…N)为终点的最长上升子序列的长度”是个好的子问题――这里把一个上升子序列中最右边的那个数,称为该子序列的“终点”。虽然这个子问题和原问题形式上并不完全一样,但是只要这N个子问题都解决了,那么这N个子问题的解中,最大的那个就是整个问题的解。
由上所述的子问题只和一个变量相关,就是数字的位置。因此序列中数的位置k 就是“状态”,而状态 k 对应的“值”,就是以ak做为“终点”的最长上升子序列的长度。这个问题的状态一共有N个。状态定义出来后,转移方程就不难想了。假定MaxLen (k)表示以ak做为“终点”的最长上升子序列的长度,那么:
MaxLen (1) = 1
MaxLen (k) = Max { MaxLen (i):1<i < k 且 ai < ak且 k≠1 } + 1
这个状态转移方程的意思就是,MaxLen(k)的值,就是在ak左边,“终点”数值小于ak,且长度最大的那个上升子序列的长度再加1。因为ak左边任何“终点”小于ak的子序列,加上ak后就能形成一个更长的上升子序列。
实际实现的时候,可以不必编写递归函数,因为从 MaxLen(1)就能推算出MaxLen(2),有了MaxLen(1)和MaxLen(2)就能推算出MaxLen(3)……

#include <stdio.h>
#define  MAX 1000
int seq[MAX+10];
int seqlen[MAX+10];
int main()
{
	int i,j,k,N,max,maxlen=1;
	for(i=1;i<=9;i++)
		seqlen[i]=1;               //seqlen数组存以第i个数为终点的最长上升序列
	scanf("%d",&N);
	for(i=1;i<=N;i++)
		scanf("%d",&seq[i]);       //seq数组保存序列数组
	for (i=2;i<=N;i++)
	{
		max=0;
		for (j=1;j<=i-1;j++)
		{
			if(seq[j]<seq[i]&&seqlen[j]>max)  //在前i-1个序列中,寻找以终点小于seq[i]的最长的子序列,即最优子状态
				max=seqlen[j];
		}
		seqlen[i]=max+1;
		if(seqlen[i]>maxlen)           //seqlen中保存的是第i个数为终点的最长上升序列,找出这个数组中最大的值即为最优序列长度
			maxlen=seqlen[i];
	}
	printf("%d/n",maxlen);
	return 0;
}

最长上升子序列nlogn算法

假设存在一个序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出来它的LIS长度为5。n
下面一步一步试着找出它。
我们定义一个序列B,然后令 i = 1 to 9 逐个考察这个序列。
此外,我们用一个变量Len来记录现在最长算到多少了

首先,把d[1]有序地放到B里,令B[1] = 2,就是说当只有1一个数字2的时候,长度为1的LIS的最小末尾是2。这时Len=1

然后,把d[2]有序地放到B里,令B[1] = 1,就是说长度为1的LIS的最小末尾是1,d[1]=2已经没用了,很容易理解吧。这时Len=1

接着,d[3] = 5,d[3]>B[1],所以令B[1+1]=B[2]=d[3]=5,就是说长度为2的LIS的最小末尾是5,很容易理解吧。这时候B[1..2] = 1, 5,Len=2

再来,d[4] = 3,它正好加在1,5之间,放在1的位置显然不合适,因为1小于3,长度为1的LIS最小末尾应该是1,这样很容易推知,长度为2的LIS最小末尾是3,于是可以把5淘汰掉,这时候B[1..2] = 1, 3,Len = 2

继续,d[5] = 6,它在3后面,因为B[2] = 3, 而6在3后面,于是很容易可以推知B[3] = 6, 这时B[1..3] = 1, 3, 6,还是很容易理解吧? Len = 3 了噢。

第6个, d[6] = 4,你看它在3和6之间,于是我们就可以把6替换掉,得到B[3] = 4。B[1..3] = 1, 3, 4, Len继续等于3

第7个, d[7] = 8,它很大,比4大,嗯。于是B[4] = 8。Len变成4了

第8个, d[8] = 9,得到B[5] = 9,嗯。Len继续增大,到5了。

最后一个, d[9] = 7,它在B[3] = 4和B[4] = 8之间,所以我们知道,最新的B[4] =7,B[1..5] = 1, 3, 4, 7, 9,Len = 5。

于是我们知道了LIS的长度为5。

!!!!! 注意。这个1,3,4,7,9不是LIS,它只是存储的对应长度LIS的最小末尾。有了这个末尾,我们就可以一个一个地插入数据。虽然最后一个d[9] = 7更新进去对于这组数据没有什么意义,但是如果后面再出现两个数字 8 和 9,那么就可以把8更新到d[5], 9更新到d[6],得出LIS的长度为6。

然后应该发现一件事情了:在B中插入数据是有序的,而且是进行替换而不需要挪动——也就是说,我们可以使用二分查找,将每一个数字的插入时间优化到O(logN)~~~~~于是算法的时间复杂度就降低到了O(NlogN)~!

#include <iostream>
#include<cstdio>
#include<string.h>
using namespace std;
#define Maxn 50010

typedef long long ll;
ll arr[Maxn],ans[Maxn],len;



int main()
{
    ll p,i,j,k;
    //scanf("%d",&T);
    //while(T--)
    //{
        scanf("%lld",&p);
        for(i=1;i<=p;i++)
        {
            scanf("%lld",&arr[i]);

        }
        ans[1]=arr[1];
        len=1;
        for(i=2;i<=p;i++)
        {
            if(arr[i]>ans[len])
                ans[++len]=arr[i];
            else{
                ll pos =lower_bound(ans+1,ans+len,arr[i])-ans;
                ans[pos]=arr[i];
            }

        }
        printf("%lld\n",len);
   // }
    return 0;
}

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

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/132293.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • html限制输入文本长度_get请求url长度限制多少位

    html限制输入文本长度_get请求url长度限制多少位http响应报文,如果响应的内容比较大,客户端怎么样判断接收完了呢?1.http协议有正文大小说明的content-length2.或者分块传输chunked的话读到0\r\n\r\n就是读完了http响应内容比较大的话,会分成多个tcpsegment发送,不是最后一个segment的话,tcp的payload不会有httpheader字段,如果是最后一个tcpse…

  • Keil5新建STM32工程(详细图解)[通俗易懂]

    Keil5新建STM32工程(详细图解)[通俗易懂]一、下载ST官方库    首先注册账号登录,然后点击产品、微控制器;如图       接着右侧的工具与软件;嵌入式软件,并选择STM32,如图。 我的是STM32F1系列,选择如图这个   获取软件   然后下载即可。 二、安装keil   不介绍。相信都安装好了。三、准备工作       新建project文件夹,并在里面新建三个文件夹,CMSIS(存放内核函数及启动引导文件)…

  • word-wrap同word-break的区别

    word-wrap同word-break的区别

  • js匿名函数和命名函数_jsp调用java方法

    js匿名函数和命名函数_jsp调用java方法由衷的感叹,js真是烦。学到现在,渐渐理解了什么是:语言都是通用的,没有好不好,只有擅长不擅长。继承,多态,甚至指针,c能实现,c++,java有,javascript(和java是雷锋和雷峰塔的区别,名字上不知道坑了多少人)也能变通实现。温故知新,今天又回味了一遍,匿名函数作为函数参数。代码很短,五脏俱全。functiontest(a,b){a+=1;b(a);}test(3,func…

  • 谁有FlashFXP可用注册码

    谁有FlashFXP可用注册码 急用,谢了

  • Mysql表分区(diskgenius分区教程)

    Mysql表分区(diskgenius分区教程)一、MySQL分区表介绍分区是一种表的设计模式,正确的分区可以极大地提升数据库的查询效率,完成更高质量的SQL编程。但是如果错误地使用分区,那么分区可能带来毁灭性的的结果。分区功能并不是在存储引擎层完成的,因此不只有InnoDB存储引擎支持分区,常见的存储引擎MyISAM、NDB等都支持分区。但是并不是所有的存储引擎都支持,如CSV、FEDORATED、MERGE等就不支持分区。在

发表回复

您的电子邮箱地址不会被公开。

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