1. 程式人生 > >最長遞增子序列(LIS)

最長遞增子序列(LIS)

本篇部落格主要講述什麼是最長公共子序列、求解最長公共子序列的思想,以及程式碼。

什麼是最長公共子序列?

  給定一個長度為N的陣列,找出一個最長的單調自增子序列(不要求是連續的)。例如:6 5 7 8 4 3 9 1,這裡的最長遞增子序列是{ 6, 7, 8, 9}或者{5, 7, 8, 9}。可以看出最長遞增子序列不唯一,但是長度一定是唯一的,不然也不能叫最長遞增子序列。

動態規劃的思想實現:

if(a[i] > a[j] && dp[i] < dp[j] + 1)
                dp[i] = dp[j] + 1;

要求第i個遞增子序列,就得先求第i-1,求第i-1個就得求i-2.....以此類推,實現方法就是不斷遍歷,去查詢i前面的每一個dp,然後去進行比較,如果滿足以上條件就進行跟新。

void LIS()
{
    for(int i = 0; i < n; i++)//初始化dp[i] = 1;
    {
        dp[i] = 1;
    }
    for(int i = 1; i < n; i++)//遍歷
    {
        for(int j = 0; j < i; j++)//從i前面的查詢
        {
            if(a[i] > a[j] && dp[i] < dp[j] + 1)//dp[i] < dp[j] + 1的作用就是更新最大的一個遞增
                dp[i] = dp[j] + 1;
        }
    }
}

二分的思想實現:

  提到二分的思想就不得不想到STL中比較好用的lower_bound(start,end, elem)函式,lowe_bound的作用就是利用二分的思想去查詢第一個小於等於elem的數。

  二分上的思想去實現最長遞增子序列它的時間複雜度是O(N * logN),在第二種演算法中,在計算每一個dp(i)時,都要找出最大的dp(j)(j<i)來,由於dp(j)沒有順序,只能順序查詢滿足aj<ai最大的dp(j),如果能將讓f(j)有序,就可以使用二分查詢,這樣演算法的時間複雜度就可能降到O(nlogn)。於是想到用一個數組B來儲存“子序列的”最大遞增子序列的最末元素,即有B[dp(j)] = aj在計算f(i)時,在陣列B中用二分查詢法找到滿足j<i且B[dp(j)]=aj<ai的最大的j,並將B[dp[j]+1]置為ai。

其實就是相當於,在B中先更新一個,然後在跟新第i個,然後在B中查詢有第i在元素比在哪個位置的元素大,如果i個元素元素的元素比最後一個大那麼最後一個元素新增到最後位置,如果在中間的話,就用第i個元素取代第B中第一個大於等於它的那個數的位置。

void LIS()
{
    for(int i = 0; i < n; i++)
    {

        dp[i] = INF;
    }
    for(int i = 0; i < n; i++)
    {
        *lower_bound(dp, dp + n, a[i]) = a[i];
    }
}

例題:

最少攔截系統 hdu 1257

狀態規劃:

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
#define N 100000
int a[N], dp[N];
int main(){
    int n;
    while(~scanf("%d", &n)){
        for(int i = 0; i < n; i++){
        cin>>a[i];
        dp[i] = 1;
    }
    for(int i = 1; i < n; i++){
        for(int j = 0; j < i; j++){
          if(a[i] > a[j] && dp[i] < dp[j] + 1)
                dp[i] = dp[j] + 1;
        }
    }
    int maxx = -1;
    for(int i = 0; i < n; i++){
        if(dp[i] > maxx){
            maxx = dp[i];
        }
    }
    cout<<maxx<<endl;
    }

return 0;
}

二分:

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
#define N 100000
#define INF 10000000
int a[N], dp[N];
int main(){
    int n;
    while(~scanf("%d", &n)){
        for(int i = 0; i < n; i++){
            cin>>a[i];
            dp[i] = INF;
        }
        for(int i = 0; i < n; i++){
            *lower_bound(dp, dp + n, a[i]) = a[i];
        }
        cout<<lower_bound(dp, dp+n, INF) - dp<<endl;
    }
return 0;
}