LIS的长度 及 输出结果

首先来看一道经典例题吧(导弹拦截)

题意

\(n\) 个导弹依次飞来,每次拦截的导弹都不能高于前一发的高度,求一次最多可以拦截多少导弹

题目数据范围:\(n \leq 10^5\)

求LIS的长度

\(O(n^2)\) 做法

\(dp[i]\) 表示拦截第 \(i\) 枚导弹的情况下,最多拦截导弹的个数

容易想到转移 \(dp[i] =\) max{ $ dp[j] + 1 $ }

容易看出,时间复杂度是 \(O(n^2)\)

\(O(nlogn)\) 做法

用二分查找维护另一个序列 \(b[]\)

具体见代码:

void solve() {
    for(int i = 1; i <= n; i++) {
        tem[i] = a[i];
    }
    for(int i = 1; i <= n; i++) {
        a[i] = tem[n + 1 - i];
    }
    // 最长不下降子序列
    len = 0;
    b[++len] = a[1];
    for(int i = 2; i <= n; i++) {
        if(a[i] >= b[len]) {
            b[++len] = a[i];
        }
        else {
            int id = upper_bound(b + 1, b + len + 1, a[i]) - b;
            b[id] = a[i];
        }
    }
    cout << len << endl;

    // 最长上升子序列
    for(int i = 1; i <= n; i++) {
        a[i] = tem[i];
    }
    len = 0;
    b[++len] = a[1];
    for(int i = 1; i <= n; i++) {
        if(a[i] > b[len]) {
            b[++len] = a[i];
        }
        else {
            int id = lower_bound(b + 1, b + len + 1, a[i]) - b;
            b[id] = a[i];
        }
    }
    cout << len << endl;
}

但是注意,这里的 \(b[]\) 存的并不是真正的最长子序列,只有长度是正确的

输出LIS具体序列

上面的求长度的两种方法就行不通啦。

再引入一个数组 \(pos[]\),存储每个数被放进 \(b[]\) 数组时所在的位置

然后就可以输出合法的LIS具体序列

例如,要在求出最长LIS的同时,需要求出最短的包含这个LIS的区间长度

// 第一个问题:最长序列的长度
// 第二个问题:输出最长的序列(同时要求包含这个序列的区间长度最短)
void solve() {
    len = 0;
    b[++len] = a[1];
    pos[1] = 1;
    v[1].push_back(1);
    for(int i = 2; i <= n; i++) {
        if(a[i] >= b[len]) {
            b[++len] = a[i];
            pos[i] = len; 
        }
        else {
            int id = upper_bound(b + 1, b + len + 1, a[i]) - b;
            b[id] = a[i];
            pos[i] = id;
        }
        // cout << i <<' '<<a[i]<<' '<<pos[i]<<endl;
        v[pos[i]].push_back(i);
    }
    printf("%d ", len);

    int ans = 1e9;
    for(auto i : v[len]) {   // i : 
        int pos = i;
        for(int j = len - 1; j >= 1; j--) {
            int p = lower_bound(v[j].begin(), v[j].end(), pos) - v[j].begin();
            p -= 1;
            pos = v[j][p];
        }
        ans = min(ans, i - pos + 1);
    }
    printf("%d\n", ans);
}
posted @ 2022-10-01 21:04  starlightlmy  阅读(64)  评论(0)    收藏  举报