acwing 896最長昇順部分列II(2点単調最適化)

トピック

ここに画像の説明を挿入します

回答

  1. 最適化されていない最長昇順部分列問題の場合、f [i]を使用してiで終わる最長昇順部分列を表し、iの前の数値を列挙してf [i]を更新します。これはO(n 2)であり、この質問のデータ範囲は広く、タイムアウトします

ここに画像の説明を挿入します
最適化されたバージョンは実際には貪欲なアイデアです。同じ長さの昇順のサブシーケンス、1 35と12 7の場合、実際には終了番号を小さくするだけで済みます。これは、終了が小さいほど、次回更新する可能性が高くなるためです。たとえば、次の数値が6の場合、1 35は13 5 6に更新できますが、1 2 7は更新されないため、f配列を使用して、長さ1 2 3 ...の昇順のサブシーケンスを記録できます。 f [1] = 1などの終了番号。また、f配列も単調に増加していることがわかります(自分で証明できます)

  1. 次に、a [i]を前から後ろに列挙するたびに、二分法でaf [mid]を見つけることができるため、a [i]はf [mid]より大きい最小値、つまりf [mid +1]になります。 > a [i]> f [mid]の場合、長さはr + 1になり、f [r +1]の位置の値を更新してできるだけ小さくします。
  1. 最後のlenは、最長の昇順サブシーケンスの長さ、列挙O(n)、2部O(logn)、合計時間計算量O(nlogn)です。

コード

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 1e5 + 10;

int n;
int a[N];
int f[N];


int main() {
    
    

    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];

    int len = 0;
    for (int i = 1; i <= n; i++) {
    
    
        int l = 0, r = len;
        while (l < r) {
    
    
            int mid = (l + r + 1) >> 1;
            if (f[mid] < a[i]) l = mid;
            else r = mid - 1;
        }
        len = max(len, r + 1);
        f[r + 1] = a[i];
    }
    cout<<len<<endl;


    return 0;
}


おすすめ

転載: blog.csdn.net/qq_44791484/article/details/114823416