最長上升子序列 (LIS) 詳解+例題模板 (全)

歡迎訪問https://blog.csdn.net/lxt_Lucia~~

宇宙第一小仙女\(^o^)/~~萌量爆表求帶飛=≡Σ((( つ^o^)つ~ dalao們點個關注唄~~

 

最長上升子序列(LIS)詳解求法+例題模板

 

1.摘要:

       關於LIS部分,本篇博客講一下LIS的概念定義和理解,以及求LIS的三種方法,分別是O(n^2)的DP,O(nlogn)的二分+貪心法,以及O(nlogn)的樹狀數組優化的DP,最後附上幾道非常經典的LIS的例題及分析。

 

2.LIS的定義:

        最長上升子序列(longest  increasing subsequence),也可以叫最長非降序子序列,簡稱LIS,不是太難。即一個數的序列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)。

        是不是覺得好抽象~沒事我給你解釋~

        首先需要知道,子串子序列的概念,我們以字符子串和字符子序列爲例,更爲形象,也能順帶着理解字符的子串和子序列:

     (1)字符子串指的是字符串中連續的n個字符,如abcdefg中,ab,cde,fg等都屬於它的字串。

     (2)字符子序列指的是字符串中不一定連續但先後順序一致的n個字符,即可以去掉字符串中的部分字符,但不可改變其前後順序。如abcdefg中,acdg,bdf屬於它的子序列,而bac,dbfg則不是,因爲它們與字符串的字符順序不一致。

       知道了這個,數值的子序列就很好明白了,即用數組成的子序列。這樣的話,最長上升子序列也很容易明白了,歸根結底還是子序列,然後子序列中,按照上升順序排列的最長的就是我們最長上升子序列了,這樣聽來是不是就很容易明白啦~

       還有一個非常重要的問題:請大家用集合的觀點來理解這些概念,子序列、公共子序列以及最長公共子序列都不唯一,但很顯然,對於固定的兩個數組,雖然最LIS不一定唯一,但LIS的長度是一定的

 

3.LIS長度的求解方法:

那麼這個到底該怎麼求呢?

這裏詳細介紹一下求LIS的三種方法,分別是O(n^2)的DP,O(nlogn)的二分+貪心法,以及O(nlogn)的樹狀數組優化的DP。

 

解法1:動態規劃:

  我們都知道,動態規劃的一個特點就是當前解可以由上一個階段的解推出, 由此,把我們要求的問題簡化成一個更小的子問題。子問題具有相同的求解方式,只不過是規模小了而已。最長上升子序列就符合這一特性。我們要求n個數的最長上升子序列,可以求前n-1個數的最長上升子序列,再跟第n個數進行判斷。求前n-1個數的最長上升子序列,可以通過求前n-2個數的最長上升子序列……直到求前1個數的最長上升子序列,此時LIS當然爲1。

  讓我們舉個例子:求 2 7 1 5 6 4 3 8 9 的最長上升子序列。我們定義d(i) (i∈[1,n])來表示前i個數以A[i]結尾的最長上升子序列長度。

  前1個數 d(1)=1 子序列爲2;

  前2個數 7前面有2小於7 d(2)=d(1)+1=2 子序列爲2 7

  前3個數 在1前面沒有比1更小的,1自身組成長度爲1的子序列 d(3)=1 子序列爲1

  前4個數 5前面有2小於5 d(4)=d(1)+1=2 子序列爲2 5

  前5個數 6前面有2 5小於6 d(5)=d(4)+1=3 子序列爲2 5 6

  前6個數 4前面有2小於4 d(6)=d(1)+1=2 子序列爲2 4

  前7個數 3前面有2小於3 d(3)=d(1)+1=2 子序列爲2 3

  前8個數 8前面有2 5 6小於8 d(8)=d(5)+1=4 子序列爲2 5 6 8

  前9個數 9前面有2 5 6 8小於9 d(9)=d(8)+1=5 子序列爲2 5 6 8 9

  d(i)=max{d(1),d(2),……,d(i)} 我們可以看出這9個數的LIS爲d(9)=5

  總結一下,d(i)就是找以A[i]結尾的,在A[i]之前的最長上升子序列+1,當A[i]之前沒有比A[i]更小的數時,d(i)=1。所有的d(i)裏面最大的那個就是最長上升子序列。其實說的通俗點,就是每次都向前找比它小的和比它大的位置,將第一個比它大的替換掉,這樣操作雖然LIS序列的具體數字可能會變,但是很明顯LIS長度還是不變的,因爲是替換掉了,並沒有改變增加或者減少長度。但是我們通過這種方式是無法求出最長上升子序列具體是什麼的,這點和最長公共子序列不同,我也沒有想出什麼好的方法,如果有dadadadadalao想出來了,歡迎私聊分享哦~

這裏寫圖片描述 
這裏寫圖片描述 
這裏寫圖片描述

狀態設計:F[ i ]代表以A[ i ]結尾的LIS的長度

狀態轉移:F[ i ]=max{ F[ j ]+1 ,F[ i ] }(1<=j< i,A[j]< A[i])

邊界處理:F[ i ]=1(1<=i<=n)

時間複雜度:O(n^2)

話不多說,show me the code!

代碼實現:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 103,INF=0x7f7f7f7f;
int a[maxn],f[maxn];
int n,ans=-INF;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&a[i]);
        f[i]=1;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<i;j++)
            if(a[j]<a[i]) f[i]=max(f[i],f[j]+1);
    for(int i=1;i<=n;i++) 
        ans=max(ans,f[i]);
    printf("%d\n",ans);
    return 0;
}

這個算法的時間複雜度爲〇(n²),並不是最優的算法。在限制條件苛刻的情況下,這種方法行不通。那麼怎麼辦呢!有沒有時間複雜度更小的算法呢?說到這裏了,當然是有的啦!還有一種時間複雜度爲〇(nlogn)的算法,下面就來看看。

 

解法2:貪心+二分:

思路:

新建一個low數組,low[i]表示長度爲i的LIS結尾元素的最小值。對於一個上升子序列,顯然其結尾元素越小,越有利於在後面接其他的元素,也就越可能變得更長。因此,我們只需要維護low數組,對於每一個a[i],如果a[i] > low[當前最長的LIS長度],就把a[i]接到當前最長的LIS後面,即low[++當前最長的LIS長度]=a[i]。 
那麼,怎麼維護low數組呢? 
對於每一個a[i],如果a[i]能接到LIS後面,就接上去;否則,就用a[i]取更新low數組。具體方法是,在low數組中找到第一個大於等於a[i]的元素low[j],用a[i]去更新low[j]。如果從頭到尾掃一遍low數組的話,時間複雜度仍是O(n^2)。我們注意到low數組內部一定是單調不降的,所有我們可以二分low數組,找出第一個大於等於a[i]的元素。二分一次low數組的時間複雜度的O(lgn),所以總的時間複雜度是O(nlogn)。

  我們再舉一個例子:有以下序列A[ ]=3 1 2 6 4 5 10 7,求LIS長度。

  我們定義一個B[i]來儲存可能的排序序列,len爲LIS長度。我們依次把A[i]有序地放進B[i]裏。(爲了方便,i的範圍就從1~n表示第i個數)

  A[1]=3,把3放進B[1],此時B[1]=3,此時len=1,最小末尾是3

  A[2]=1,因爲1比3小,所以可以把B[1]中的3替換爲1,此時B[1]=1,此時len=1,最小末尾是1

  A[3]=2,2大於1,就把2放進B[2]=2,此時B[]={1,2},len=2

  同理,A[4]=6,把6放進B[3]=6,B[]={1,2,6},len=3

  A[5]=4,4在2和6之間,比6小,可以把B[3]替換爲4,B[]={1,2,4},len=3

  A[6]=5,B[4]=5,B[]={1,2,4,5},len=4 

  A[7]=10,B[5]=10,B[]={1,2,4,5,10},len=5

  A[8]=7,7在5和10之間,比10小,可以把B[5]替換爲7,B[]={1,2,4,5,7},len=5

  最終我們得出LIS長度爲5。但是,但是!!這裏的1 2 4 5 7很明顯並不是正確的最長上升子序列。是的,B序列並不表示最長上升子序列,它只表示相應最長子序列長度的排好序的最小序列。這有什麼用呢?我們最後一步7替換10並沒有增加最長子序列的長度,而這一步的意義,在於記錄最小序列,代表了一種“最可能性”。假如後面還有兩個數據8和9,那麼B[6]將更新爲8,B[7]將更新爲9,len就變爲7。讀者可以自行體會它的作用。

  因爲在B中插入的數據是有序的,不需要移動,只需要替換,所以可以用二分查找插入的位置,那麼插入n個數的時間複雜度爲〇(logn),這樣我們會把這個求LIS長度的算法複雜度降爲了〇(nlogn)。話不多說了,show me the code!

代碼實現:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn =300003,INF=0x7f7f7f7f;
int low[maxn],a[maxn];
int n,ans;
int binary_search(int *a,int r,int x)
//二分查找,返回a數組中第一個>=x的位置 
{
    int l=1,mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        if(a[mid]<=x)
            l=mid+1;
        else 
            r=mid-1;
    }
    return l;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&a[i]); 
        low[i]=INF;//由於low中存的是最小值,所以low初始化爲INF 
    }
    low[1]=a[1]; 
    ans=1;//初始時LIS長度爲1 
    for(int i=2;i<=n;i++)
    {
        if(a[i]>=low[ans])//若a[i]>=low[ans],直接把a[i]接到後面 
            low[++ans]=a[i];
        else //否則,找到low中第一個>=a[i]的位置low[j],用a[i]更新low[j] 
            low[binary_search(low,ans,a[i])]=a[i];
    }
    printf("%d\n",ans);//輸出答案 
    return 0;
}

這其中用到了二分查找第一個大於等於的,其實C++裏面的有一個函數可用代替二分,那就是——low_bound( )函數。

lower_bound( )函數:

下面是使用lower_bound優化最長上升子序列。由於長度相同的上升子序列只需要保存結尾最小的那個,而長度遞增時,結尾數字的大小也是遞增的。最長上升子序列就是找出比他大的第一個數。前面的數都比他小,所以他和這個數的長度相同。然後由於他比較然後小,更新找到的那個值。

#include<stdio.h>  
#include<string.h>  
#include<algorithm>  
  
using namespace std;  
  
int num[10]={3,6,3,2,4,6,7,5,4,3};  
  
const int INF=0x3f3f3f3f;  
int l=10;  
int g[100];  
int d[100];  
int main()  
{  
    fill(g,g+l,INF);  
    int max_=-1;  
    for(int i=0;i<l;i++)  
    {  
        int j=lower_bound(g,g+l,num[i])-g;  
        d[i]=j+1;  
        if(max_<d[i])  
            max_=d[i];  
        g[j]=num[i];  
    }  
    printf("%d\n",max_);  
    return 0;  
}  
  
這個算法其實已經不是DP了,有點像貪心。至於複雜度降低其實是因爲這個算法裏面用到了二分搜索。本來有N個數要處理是O(n),每次計算要查找N次還是O(n),一共就是O(n^2);現在搜索換成了O(logn)的二分搜索,總的複雜度就變爲O(nlogn)了。這裏主要注意一下lower_bound函數的應用,注意減去的g是地址。

 

解法3:樹狀數組維護:

我們再來回顧O(n^2)DP的狀態轉移方程:F[i]=max{F[j]+1}(1<=j< i,A[j]< A[i]) 
我們在遞推F數組的時候,每次都要把F數組掃一遍求F[j]的最大值,時間開銷比較大。我們可以藉助數據結構來優化這個過程。用樹狀數組來維護F數組(據說分塊也是可以的,但是分塊是O(n*sqrt(n))的時間複雜度,不如樹狀數組跑得快),首先把A數組從小到大排序,同時把A[i]在排序之前的序號記錄下來。然後從小到大枚舉A[i],每次用編號小於等於A[i]編號的元素的LIS長度+1來更新答案,同時把編號小於等於A[i]編號元素的LIS長度+1。因爲A數組已經是有序的,所以可以直接更新。有點繞,具體看代碼。

還有一點需要注意:樹狀數組求LIS不去重的話就變成了最長不下降子序列了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn =103,INF=0x7f7f7f7f;
struct Node{
    int val,num;
}z[maxn]; 
int T[maxn];
int n;
bool cmp(Node a,Node b)
{
    return a.val==b.val?a.num<b.num:a.val<b.val;
}
void modify(int x,int y)//把val[x]替換爲val[x]和y中較大的數 
{
    for(;x<=n;x+=x&(-x)) T[x]=max(T[x],y);
}
int query(int x)//返回val[1]~val[x]中的最大值 
{
    int res=-INF;
    for(;x;x-=x&(-x)) res=max(res,T[x]);
    return res;
}
int main()
{
    int ans=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&z[i].val);
        z[i].num=i;//記住val[i]的編號,有點類似於離散化的處理,但沒有去重 
    }
    sort(z+1,z+n+1,cmp);//以權值爲第一關鍵字從小到大排序 
    for(int i=1;i<=n;i++)//按權值從小到大枚舉 
    {
        int maxx=query(z[i].num);//查詢編號小於等於num[i]的LIS最大長度
        modify(z[i].num,++maxx);//把長度+1,再去更新前面的LIS長度
        ans=max(ans,maxx);//更新答案
    }
    printf("%d\n",ans);
    return 0;
}

 

 

4.經典例題模板:

 

例1:Super Jumping! Jumping! Jumping! 

Description

Nowadays, a kind of chess game called “Super Jumping! Jumping! Jumping!” is very popular in HDU. Maybe you are a good boy, and know little about this game, so I introduce it to you now. 



The game can be played by two or more than two players. It consists of a chessboard(棋盤)and some chessmen(棋子), and all chessmen are marked by a positive integer or “start” or “end”. The player starts from start-point and must jumps into end-point finally. In the course of jumping, the player will visit the chessmen in the path, but everyone must jumps from one chessman to another absolutely bigger (you can assume start-point is a minimum and end-point is a maximum.). And all players cannot go backwards. One jumping can go from a chessman to next, also can go across many chessmen, and even you can straightly get to end-point from start-point. Of course you get zero point in this situation. A player is a winner if and only if he can get a bigger score according to his jumping solution. Note that your score comes from the sum of value on the chessmen in you jumping path. 
Your task is to output the maximum value according to the given chessmen list. 

Input

Input contains multiple test cases. Each test case is described in a line as follow: 
N value_1 value_2 …value_N 
It is guarantied that N is not more than 1000 and all value_i are in the range of 32-int. 
A test case starting with 0 terminates the input and this test case is not to be processed. 

Output

For each case, print the maximum according to rules, and one line one case. 

Sample Input

3 1 3 2
4 1 2 3 4
4 3 3 2 1
0

Sample Output

4
10
3

思路:

題意是有N個數字構成的序列,求最大遞增子段和,即遞增子序列和的最大值,思路就是定義dp[i],表示以a[i]結尾的最大遞增子段和,雙重for循環,每次求出以a[i]結尾的最大遞增子段和。

 

代碼:

#include<math.h>
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f

using namespace std;

int main()
{
    int a[1005],dp[1005],n,i,j,max1;
    while(scanf("%d",&n)&&n){
        max1=0;
        memset(dp,0,sizeof(dp));
        for(i=0;i<=n-1;i++)
            scanf("%d",&a[i]);
        dp[0]=a[0];
        for(i=1;i<=n-1;i++){
            for(j=0;j<=i-1;j++){
                if(a[i]>a[j])
                    dp[i]=max(dp[j]+a[i],dp[i]);
            }
            dp[i]=max(dp[i],a[i]);
        }
        max1=dp[0];
        for(i=0;i<=n-1;i++)
            max1=max(dp[i],max1);
        printf("%d\n",max1);
    }
    return 0;
}

 

例2:FatMouse's Speed

Description

FatMouse believes that the fatter a mouse is, the faster it runs. To disprove this, you want to take the data on a collection of mice and put as large a subset of this data as possible into a sequence so that the weights are increasing, but the speeds are decreasing. 

Input

Input contains data for a bunch of mice, one mouse per line, terminated by end of file. 

The data for a particular mouse will consist of a pair of integers: the first representing its size in grams and the second representing its speed in centimeters per second. Both integers are between 1 and 10000. The data in each test case will contain information for at most 1000 mice. 

Two mice may have the same weight, the same speed, or even the same weight and speed. 

Output

Your program should output a sequence of lines of data; the first line should contain a number n; the remaining n lines should each contain a single positive integer (each one representing a mouse). If these n integers are m[1], m[2],..., m[n] then it must be the case that W[m[1]] < W[m[2]] < ... < W[m[n]]  and  S[m[1]] > S[m[2]] > ... > S[m[n]] . In order for the answer to be correct, n should be as large as possible. All inequalities are strict: weights must be strictly increasing, and speeds must be strictly decreasing. There may be many correct outputs for a given input, your program only needs to find one. 

Sample Input

6008 1300
6000 2100
500 2000
1000 4000
1100 3000
6000 2000
8000 1400
6000 1200
2000 1900

Sample Output

4
4
5
9
8

思路:

題意是:給你許多組數據,沒組兩個數,一個代表老鼠的重量,一個代表老鼠的速度,爲了證明老鼠越重速度越慢,讓你取出幾組數據證明,問最多能取出幾組。體重要嚴格遞增,速度嚴格遞減,有些思維含量。

 

代碼:

#include<math.h>
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<queue>
#define INF 0x3f3f3f3f

using namespace std;

struct A{
    int w;
    int s;
    int xb;
}a[1010];

struct U{
    int num;
    int xb;
}dp[1010];

bool cmp(struct A a,struct A b){
    if(a.w==b.w)
        return a.s>b.s;
    else return a.w<b.w;
}

int main()
{
    int b[1010],i,j,k,n=0,max1=0;
    while(scanf("%d%d",&a[n].w,&a[n].s)!=EOF){
        ++n;
        a[n-1].xb=n;
    }
    sort(a,a+n,cmp);
    for(i=0;i<=n-1;i++){
        dp[i].num=1;
        dp[i].xb=0;
    }
    for(i=1;i<=n-1;i++){
        for(j=0;j<=i-1;j++){
            if(a[i].w>a[j].w&&a[i].s<a[j].s&&dp[i].num<dp[j].num+1){
                dp[i].num=dp[j].num+1;
                dp[i].xb=j;
            }
        }
        if(dp[i].num>max1)
        {
            max1=dp[i].num;
            k=i;
        }
    }
    for(i=1;i<=max1;i++)
    {
        b[i]=k;
        k=dp[k].xb;
    }
    printf("%d\n",max1);
    for(i=max1;i>=1;i--)
        printf("%d\n",a[b[i]].xb);
    return 0;
}

 

例3:最少攔截系統

Decription

某國爲了防禦敵國的導彈襲擊,發展出一種導彈攔截系統.但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能超過前一發的高度.某天,雷達捕捉到敵國的導彈來襲.由於該系統還在試用階段,所以只有一套系統,因此有可能不能攔截所有的導彈. 
怎麼辦呢?多搞幾套系統唄!你說說倒蠻容易,成本呢?成本是個大問題啊.所以俺就到這裏來求救了,請幫助計算一下最少需要多少套攔截系統. 

Input

輸入若干組數據.每組數據包括:導彈總個數(正整數),導彈依此飛來的高度(雷達給出的高度數據是不大於30000的正整數,用空格分隔) 

Output

對應每組數據輸出攔截所有導彈最少要配備多少套這種導彈攔截系統. 

Sample Input

8 389 207 155 300 299 170 158 65

Sample Output

2

思路:

這題是一個貪心+LIS,用dp數組存儲已經部署好的防禦系統能打的最大高度,每來一個新的導彈,判斷之前已經部署好的防禦系統能否打下當前導彈,如果能的話就選那個最垃圾的防禦系統來攻擊導彈,如果之前已經部署的最厲害的防禦系統也打不下來的話,那麼就新部署一個攔截系統來攔截當前導彈。

 

代碼:

#include<cmath>
#include<deque>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#define INS 0x3f3f3f3f
#define eps 1e-10

using namespace std;

int a[10001],dp[10001];

int main()
{
    int n,i,j,k,z,min1;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=0; i<=n-1; i++)
            scanf("%d",&a[i]);
        memset(dp,0,sizeof(dp));
        k=1;
        dp[0]=a[0];
        for(i=1; i<=n-1; i++)
        {
            z=-1;
            min1=0x3f3f3f3f;
            for(j=0; j<=k-1; j++)
                if(dp[j]<min1&&a[i]<=dp[j])
                {
                    z=j;
                    min1=dp[j];
                }
            if(z==-1)
                dp[k++]=a[i];
            else
                dp[z]=a[i];
        }
        printf("%d\n",k);
    }
    return 0;
}

 

例4:Bridging signals

Description

'Oh no, they've done it again', cries the chief designer at the Waferland chip factory. Once more the routing designers have screwed up completely, making the signals on the chip connecting the ports of two functional blocks cross each other all over the place. At this late stage of the process, it is too 
expensive to redo the routing. Instead, the engineers have to bridge the signals, using the third dimension, so that no two signals cross. However, bridging is a complicated operation, and thus it is desirable to bridge as few signals as possible. The call for a computer program that finds the maximum number of signals which may be connected on the silicon surface without rossing each other, is imminent. Bearing in mind that there may be housands of signal ports at the boundary of a functional block, the problem asks quite a lot of the programmer. Are you up to the task? 

Figure 1. To the left: The two blocks' ports and their signal mapping (4,2,6,3,1,5). To the right: At most three signals may be routed on the silicon surface without crossing each other. The dashed signals must be bridged. 

A typical situation is schematically depicted in figure 1. The ports of the two functional blocks are numbered from 1 to p, from top to bottom. The signal mapping is described by a permutation of the numbers 1 to p in the form of a list of p unique numbers in the range 1 to p, in which the i:th number pecifies which port on the right side should be connected to the i:th port on the left side. 
Two signals cross if and only if the straight lines connecting the two ports of each pair do.

Input

On the first line of the input, there is a single positive integer n, telling the number of test scenarios to follow. Each test scenario begins with a line containing a single positive integer p<40000, the number of ports on the two functional blocks. Then follow p lines, describing the signal mapping: On the i:th line is the port number of the block on the right side which should be connected to the i:th port of the block on the left side.

Output

For each test scenario, output one line containing the maximum number of signals which may be routed on the silicon surface without crossing each other.

Sample Input

4
6
4
2
6
3
1
5
10
2
3
4
5
6
7
8
9
10
1
8
8
7
6
5
4
3
2
1
9
5
8
9
2
3
1
7
4
6

Sample Output

3
9
1
4

思路:

本題爲模板題,但需注意不能用普通做法,需要用二分優化,否則會T的。

 

代碼:

#include<math.h>
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#include<queue>

using namespace std;

int a[40001],dp[40001];

int main()
{
    int T,n,i,len,pos;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(i=1; i<=n; i++)
            scanf("%d",&a[i]);
        memset(dp,0,sizeof(dp));
        len=1;dp[1]=a[1];
        for(i=2; i<=n; i++)
        {
            if(a[i]<=dp[len])
            {
                pos=lower_bound(dp,dp+len,a[i])-dp;
                dp[pos]=a[i];
            }
            else
            {
                len++;
                dp[len]=a[i];
            }
        }
        printf("%d\n",len);
    }
    return 0;
}

 

注意:

一般來說,爲防止T,都會用二分法來找,最爲簡便的就是通過lower_bound( )函數來進行查找,最常用模板請參考例題,尤其例4。.

 

 

5.相關知識:( 建議放在一起比較區分 )

1)最長公共子序列(LCS)戳這裏 ~ 

2)最長迴文子串 and 最長迴文子序列  (LPS)  戳這裏

 

 

宇宙第一小仙女\(^o^)/~~萌量爆表求帶飛=≡Σ((( つ^o^)つ~ dalao們點個關注唄~~

 

 

參考資料:

http://www.cnblogs.com/GodA/p/5180560.html

https://blog.csdn.net/George__Yu/article/details/75896330

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章