最長上升子序列LIS 詳解+變形+擴充

Iter-moon發表於2024-10-02

最長上升子序列(LIS):

定義:

最長上升子序列(LIS)是一個序列中,找到一個子序列,使得這個子序列的元素是嚴格遞增的,且該子序列的長度最大

*子串和子序列的差別:

子串: 元素的連續性,必須是相鄰的

子序列:元素的相對順序,可以不連續

從例項中來

[1, 7, 5, 6, 9, 2, 4] 這個陣列根據肉眼掃描法不難發現出LIS是[1, 5, 6, 9]長度最長為4

對於這個長度如何求解出來, 有著這幾個經典解法去求解

動態規劃 複雜度O(n ^ 2)

在計算LIS的最長長度的時候, 某些元素a[i]是LIS的一部分, 那麼a[i]以前的, 元素所有小於a[i]的元素所構成的LIS的長度就是最優的 對於動態規劃來說, 為了避免

重複問題的重複計算, 可以快取下dp[i]當前的值, dp[i]儲存的就是a[i]為結尾的最長長度, 那麼這個序列的最長長度只需去求當前dp[i]陣列的最大值即可

Code:

void LIS_dp() {
    int n;
    cin >> n;
    vector <int> a(n + 1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    vector <int> dp(n + 1, 1);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j < i; j++) {
            if (a[i] > a[j]) {
                dp[i] = max(dp[i], dp[j] + 1);
            }
        }
    }
    cout << *max_element(dp.begin() + 1, dp.end()) << '\n';
}

  

二分 複雜度(n * logn)

透過記錄一個ans陣列, ans[i]表示當前找到的長度為i + 1的上升子序列的末尾元素, ans.size()表示當前最長上升子序列的最長長度

操作:

Code:

void LIS_lower_bound() {
    int n;
    cin >> n; 
    vector <int> ans;
    for (int i = 1; i <= n; i++) {
        int x; cin >> x;
        if (ans.empty() || x > ans.back()) {
            ans.emplace_back(x);
        } else {
            auto it = lower_bound(ans.begin(), ans.end(), x) - ans.begin();
            ans[it] = x;   
        }
    }
    cout << ans.size() << '\n';
}

  

2

相關文章