求最長上升子序列LIS(兩種解法)+ LCS向LIS問題的轉化

概念 

Loggest Increasing Subsequence最長上升子序列

Loggest Common Subsequnce: 最長公共子序列(兩個或多個序列的問題)

概念就不解釋了,,,大家應該都懂

解法

O(nlog(n))的解法

這種解法是貪心+二分的思想。實際並不難,比下面的O(n^2)複雜度的解法還更容易理解。

這種算法的思想大致如下:

現在有 4 2 3 8 9 7 這個序列,下面以他爲例,說明求最長子序列的長度。

首先定義一個數組,保存這個最長的上升子序列的每一個元素,設爲dp數組。

下面我們就要依次往裏面放元素了:

        第一個元素是4,直接放進dp。                                                      || 當前dp中元素的情況:4

        第二個元素是2,比4小。把dp中的2替換成4.(下面介紹爲什麼)||當前dp中元素的情況:2

       第三個元素是3,比2大,放到dp數組中2的後面                             ||當前dp中元素的情況:2 3

       第四個元素是8,比3大,放到dp數組中3的後面                             ||當前dp中元素的情況:2 3 8

       第五個元素是9,比8大,放到dp數組中8的後面                             ||當前dp中元素的情況:2 3 8 9

       第六個元素是7,替換dp數組中的8                                                 ||當前dp中元素的情況:2 3 7 9

       最後結果:最長公共子序列的長度是4.

結論:每次進來一個數 ,他總是替換dp數組從左往右第一個大於它的數

而存到dp數組中的元素,不一定就是真實的元素。上面的例子的最長上升子序列應該是 2 3 8 9.所以這個數組只是個僞序列,真正有用的是數組的長度。

我現在只能做到這裏,證明我也不會。。。。。。。。。但就是這樣子。

O(n^2)做法

這種做法,各種數據結構的書上應該都有吧。只要找到狀態轉移方程:

dp[i]=max(dp[j])+1; //條件是1=<j<i 並且 a[i]>a[j] .

很容易就可以寫出代碼

 



LCS轉LIS

這便是這篇博客的意義所在了,LIS算法有O(nlogn)的算法,而LCS沒有。

看這樣一道題:P1439

題目描述

給出 1,2,…,n 的兩個排列P1​ 和 P2​ ,求它們的最長公共子序列。

輸入格式

第一行是一個數 n。

接下來兩行,每行爲 n個數,爲自然數 1,2,…,n 的一個排列。

輸出格式

一個數,即最長公共子序列的長度。

輸入輸出樣例

輸入 #1複製

5 
3 2 1 4 5
1 2 3 4 5

輸出 #1複製

3

說明/提示

  • 對於50% 的數據, n≤10^3;
  • 對於100% 的數據, n≤10^5。

分析:

這道題n達到了10^5,用經典的LCS解法,顯然超時。這就需要將LCS向LIS轉化

用到的思想:離散化。

舉例:如下兩個序列

4 2 3 8 9

8 4 2 3 9

做一步離散化處理,不考慮每個元素具體的值,用新數組每個元素表示原數組中元素所在的位置

1 2 3 4 5

4 1 2 3 5

說明:上述第一行表示原序列的4 2 3 8 9的每一個位置,也是就8在原序列第一個序列中的第一位、4在第1位、2在第二位、3在第三位、9在第5位。

---

理解上述的操作之後,下面觀察生成的第二個序列的規律:只要求生成的第二個序列的最長上升序列,就可以得到LCS

ok,代碼如下,僅供參考

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int MAX = 5e5+5;

int N;
int a[MAX],b[MAX];
int dp[MAX];
int belong[MAX*100]; //離散化的關鍵

int main() {
    cin >> N;
    for (int i = 1; i <= N; ++i) {
        cin >> a[i];
        belong[a[i]]=i;
    }
    for (int i = 1; i <= N; ++i) {
        cin >> b[i];
        b[i]=belong[b[i]];
    }
    int len = 0;
    memset(dp,0,sizeof(dp));
    for (int i = 1; i <= N; ++i) {
        if(dp[len]<b[i]) {
            dp[++len]=b[i];
        } else {
            *lower_bound(dp+1,dp+len+1,b[i])=b[i]; //二分查找,降低時間複雜度
        }
    }
    cout << len;
}


 

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