动态规划 | 最长上升子序列

1.问题描述
描述
一个数的序列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).

你的任务,就是对于给定的序列,求出最长上升子序列的长度。
输入
输入的第一行是序列的长度N (1 <= N <= 1000)。第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。
输出
最长上升子序列的长度。
样例输入
7
1 7 3 5 9 4 8
样例输出
4


2.解题思路

此题用动态规划来解决
第一种方法
1)找子问题
一个上升子序列中最右边的那个数,称为该子序列的“终点”。
即可转化成:
“求以ak(k=1,2,3,.....,N)为终点的最长上升子序列的长度”

虽然这个子问题和原问题形式上并不完全一样,但是只要这N个子问题都解决了,那么这N个子问题的解中,最大的那个就是整个问题的解。

2)确定状态
子问题只和一个变量(数字的位置相关),因此序列中数的位置k就是“状态”,而状态k对应的“值”,就是以ak作为“终点”的最长上升子序列的长度。
状态一共有N个。

3)找出状态转移方程
maxLen(k)表示以ak作为“终点”的最长上升子序列的长度

那么:
83B2174318194BFBA80E83B1A23DFC51.jpg

maxLen(k)的值,就是在ak左边,“终点”数值小于ak,且长度最大的那个上升子序列的长度再加1。因为ak左边任何“终点”小于ak的子序列,加上ak后就能形成一个更长的子序列。

程序如下:时间复杂度为O(N^2)

#include <iostream>
#define MaxSize 1001
using namespace std;
int MaxInArray(int * arr,int n);
int main(){
    int n;
    int MaxLen[MaxSize];//以i为终点的最长上升子序列的长度
    int array[MaxSize];
    cin >> n;
    for(int i =1;i<=n;++i){
        cin >> array[i];
        MaxLen[i] = 1;//赋初值 
    }
    for(int i=1;i<=n;++i)
//每次求以第i个数为终点的最长子序列的长度
        for(int j =1;j<=i;++j)
//察看以第j个数为终点的最长上升子序列
            if(array[i] > array[j])
                MaxLen[i] = max(MaxLen[i],MaxLen[j]+1);
    cout << MaxInArray(MaxLen,n);
    return 0;
} 
int MaxInArray(int *arr,int n){
    int Max = arr[1];
    for(int i=2;i<=n;++i)
        if(Max < arr[i])
            Max = arr[i];
    return Max;
}

第二种方法(时间复杂度为O(nlogn))
后来在遇到一道LIS的题,用上面O(nlogn)的过不了,然后了解到下面这种解法:
我们定义一个最小的有序序列lens[],其长度为len。
用题目给的1 7 3 5 9 4 8来举例。
我们定义一个数组a[7]={1,7,3,5,9,4,8}
a[1] = 1,我们把1放进lens[1],然后序列lens的长度len+1,len = 1
a[2] = 7,a[2]>lens[1],我们把7放进lens[2],然后序列lens的长度len+1,len = 2
a[3] = 3,a[3]<lens[2],因为序列lens存放的是最小的有序序列,所以我们把lens[2]替换成
a[3],此时lens[2] = 3,因为此时只是替换,并没有使lens变长,所以len不变
a[4] = 5,a[4]>lens[2],我们把5放进lens[3],然后序列lens的长度len+1,len = 3
a[5] = 9,a[5]>lens[3],我们把9放进lens[4],然后序列lens的长度len+1,len = 4
a[6] = 4,a[6]<lens[3],我们把lens[3]替换成a[6],此时lens[3] = 4,因为此时只是替换,并没有使lens变长,所以len不变
a[7] = 8,a[7]<lens[4],我们把lens[4]替换成a[7],此时lens[4] = 8,因为此时只是替换,并没有使lens变长,所以len不变
最终len为4,和我们所要求的长度结果是一致的,但是此时的有序序列不一定就是我们的最长上升子序列,此方法只能求长度,但是不能用来求真正的最长上升子序列

经过上面的例子,我们可以得出

如果a[i]>lens[len],lens[++len] = a[i]
不然则从lens这有序序列中找出一个下界x(找到一个最大的x满足len[x]<a[i]),然后将lens[x]替换为a[i],此时查找下界用二分查找,即可使整个算法的时间复杂度为O(nlogn)

改进版程序代码:

#include <iostream>
using namespace std;
int arr[100000];
int search(int l,int r,int num,int a[]);
int LIS(int n);
int main(){
    int n;
    cin >> n;
    for(int i=1;i<=n;++i)
        cin >> arr[i];
    cout << LIS(n) <<endl;
    return 0;
}
int search(int l,int r,int num,int a[]){
    int mid;
    while(l<=r){
        mid = (l+r)>>1;
        if(a[mid]<=num)
            l = mid+1;
        else
            r = mid - 1;
    }
    return l;
}
int LIS(int n){
    int * lens = new int [n+1];
    int len = 1;
    lens[1] = arr[1];
    for(int i = 2;i<=n;++i){
        if(arr[i]>lens[len])
            lens[++len] = arr[i];
        else{
            int p = search(1,len,arr[i],lens);
            lens[p] = arr[i]; 
        }
    }
    return len;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,284评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,115评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,614评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,671评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,699评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,562评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,309评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,223评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,668评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,859评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,981评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,705评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,310评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,904评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,023评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,146评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,933评论 2 355

推荐阅读更多精彩内容

  • 转自:https://blog.csdn.net/George__Yu/article/details/75896...
    laochonger阅读 1,386评论 0 1
  • 描述 给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度。 说明 最长上升子序列的定义:最长上升子序...
    6默默Welsh阅读 688评论 1 0
  • 给定一个整数序列,找到最长上升子序列(LIS),返回LIS的长度。 说明 最长上升子序列的定义: 最长上升子序列问...
    Arnold134777阅读 1,004评论 0 0
  • 问题描述 一个数的序列bi,当b1 < b2 < ... < bS的时候,我们称这个序列是上升的。对于给定的一个序...
    指尖极光阅读 355评论 0 0
  • 算法简述 最长上升子序列(Longest Increasing Subsequence, 简称LIS)是dp中比较...
    xiaoshua阅读 7,283评论 0 5