Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Lập Trình

Tìm dãy con tăng dài nhất (Longest Increasing Subsequence - LIS)


Giới thiệu

Cho một dãy số, tìm độ dài của dãy con tăng dài nhất (Longest Increasing Subsequence - LIS). Dãy con không cần phải liên tiếp, chỉ cần các phần tử theo thứ tự tăng dần.

Ví dụ: Dãy {10, 22, 9, 33, 21, 50, 41, 60, 80} có LIS là {10, 22, 33, 50, 60, 80} với độ dài 6.

Đầu vào và Đầu ra

Đầu vào: Dãy: {10, 22, 9, 33, 21, 50, 41, 60, 80}
Đầu ra: Độ dài LIS = 6, Dãy: 10 22 33 50 60 80

Thuật toán O(n²) - Quy hoạch động

LIS_DP(arr, n)
Đầu vào: Mảng arr, kích thước n.
Đầu ra: Độ dài dãy con tăng dài nhất.

Begin
    tạo mảng dp kích thước n, khởi tạo tất cả = 1
    for i := 1 to n-1, do
        for j := 0 to i-1, do
            if arr[i] > arr[j] và dp[i] < dp[j] + 1, then
                dp[i] := dp[j] + 1
        done
    done
    maxLen := max của dp
    return maxLen
End

Thuật toán O(n log n) - Nhị phân tìm kiếm (Patience Sorting)

LIS_Optimal(arr, n)
Đầu vào: Mảng arr, kích thước n.
Đầu ra: Độ dài LIS.

Begin
    tạo mảng tail rỗng
    for i := 0 to n-1, do
        x := arr[i]
        tìm vị trí pos = lower_bound(tail, x) // vị trí đầu tiên >= x
        if pos = tail.size(), then
            thêm x vào cuối tail
        else
            tail[pos] := x
    done
    return tail.size()
End

Ví dụ (C++)

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// O(n^2) DP
int lisDP(int arr[], int n) {
    vector dp(n, 1);
    for(int i = 1; i < n; i++)
        for(int j = 0; j < i; j++)
            if(arr[i] > arr[j] && dp[i] < dp[j] + 1)
                dp[i] = dp[j] + 1;
    return *max_element(dp.begin(), dp.end());
}

// O(n log n) - Binary Search
int lisOptimal(int arr[], int n) {
    vector tail;
    for(int i = 0; i < n; i++) {
        auto it = lower_bound(tail.begin(), tail.end(), arr[i]);
        if(it == tail.end())
            tail.push_back(arr[i]);
        else
            *it = arr[i];
    }
    return tail.size();
}

// Truy vết dãy LIS (từ DP)
void printLIS(int arr[], int n) {
    vector dp(n, 1), prev(n, -1);
    int maxLen = 1, maxIdx = 0;
    for(int i = 1; i < n; i++) {
        for(int j = 0; j < i; j++) {
            if(arr[i] > arr[j] && dp[i] < dp[j] + 1) {
                dp[i] = dp[j] + 1;
                prev[i] = j;
            }
        }
        if(dp[i] > maxLen) {
            maxLen = dp[i];
            maxIdx = i;
        }
    }
    vector lis;
    for(int i = maxIdx; i != -1; i = prev[i])
        lis.push_back(arr[i]);
    reverse(lis.begin(), lis.end());
    cout << "LIS: ";
    for(int x : lis) cout << x << " ";
}

int main() {
    int arr[] = {10, 22, 9, 33, 21, 50, 41, 60, 80};
    int n = 9;
    cout << "DP O(n^2): Length = " << lisDP(arr, n) << endl;
    cout << "Optimal O(n log n): Length = " << lisOptimal(arr, n) << endl;
    printLIS(arr, n);
}

Kết quả

DP O(n^2): Length = 6
Optimal O(n log n): Length = 6
LIS: 10 22 33 50 60 80