最长上升子序列

我们讨论的是严格单调递增的LIS,然后考虑基本状态$dp[i]$表示以i结尾的LIS长度。状态转移是$dp_i=\max\limits_{j<i, a_j<a_i} dp_j+1$

朴素$O(n^2)$

void solve() {
    int n;
    cin >> n;
    vector<int> dp(n + 1);
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        dp[i] = 1;
        for (int j = 1; j <= i - 1; j++) {
            if (a[j] < a[i])
                dp[i] = max(dp[i], dp[j] + 1);
        }
    }
    int ans = *max_element(alls(dp));
    cout << ans << endl;
}

用树状数组优化求dp前缀最大值,我们开一个权值树状数组,c[i]表示以值i结尾的LIS长度,每次只需要先查询后更新即可。

struct info {
    int x = 0;
    info operator+(const info &other) const {
        return {max(x, other.x)};
    }
};
void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    int ans = 0;
    Fwk<info> c(n);
    for (int i = 1; i <= n; i++) {
        int tmp = c.sum(a[i] - 1).x + 1;
        c.add(a[i], {tmp});
        deb(i, a[i], tmp);
        ans = max(ans, tmp);
    }
    cout << ans << endl;
}

当数组有比较大的时候我们使用离散化即可

struct info {
    int x = 0;
    info operator+(const info &other) const {
        return {max(x, other.x)};
    }
};

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++) cin >> a[i];
    //-----------
    vector<int> b;
    for (auto x : a) b.push_back(x);
    sort(alls(b));
    b.erase(unique(alls(b)), b.end());
    auto find = [&](int x) {
        return lower_bound(alls(b), x) - b.begin() + 1;
    };
    //-----------
    int ans = 0;
    Fwk<info> c(n);
    for (int i = 1; i <= n; i++) {
        int pos = find(a[i]);
        int tmp = c.sum(pos - 1).x + 1;
        c.add(pos, {tmp});
        ans = max(ans, tmp);
    }
    cout << ans << endl;
}

贪心加二分解决这个问题:维护一个vector,dp[i]表示长度为i的LIS的结尾元素的最小值。 然后考虑如何转移:每次二分更新即可

void solve() {
    int n;
    cin >> n;
    vector<int> a(n + 1), dp(n + 1);
    int len = 0;
    for (int i = 1; i <= n; i++) cin >> a[i];
    dp[0]=-inf;
    for (int i = 1; i <= n; i++) {
        if (a[i] > dp[len]) {
            len++;
            dp[len] = a[i];
        } else {
            int pos = lower_bound(dp.begin() + 1, dp.begin() + len + 1, a[i]) - dp.begin();
            dp[pos] = a[i];
        }
    }
    cout << len << endl;
}