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);
}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号