最大递增子序列LIS(三种方法+stl二分纠错(包括反向迭代))

博客探讨了如何找到最大递增子序列(LIS),包括三种不同的方法,并重点讲解了STL中lower_bound和upper_bound的使用,特别是如何在反向迭代中进行二分查找。示例代码展示了如何利用这些工具来高效解决LIS问题,尽管回溯可能不是所有方法都适用。
摘要由CSDN通过智能技术生成

//之前upper_bound lower_bound搞错了
// upper_bound( first, last, & val)算法返回一个非递减序列[first, last)中第一个 大于 val的地址。
// lower_bound( first, last, & val)算法返回一个非递减序列[first, last)中第一个 大于等于 值val的地址。
// upper_bound( first, last, & val, greater())算法返回一个非递增序列[first, last)中第一个 小于 val的地址。
// lower_bound( first, last, & val, greater())算法返回一个非递增序列[first, last)中第一个 小于等于 值val的地址。
// int pos = lower_bound(age.rbegin(), age.rend(), a[i]) - age.rbegin();
//rbegin rend反向迭代器(倒着查找)

1、把数组复制后排序,然后求其与原数组的LCS

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <vector>

using namespace std;

const int INF = 0x3f3f3f3f;
const int MAXN = 10005;
int a[MAXN], b[MAXN];
int dp[2][MAXN];
int n, m;
void lcs() {
    memset(dp, 0, sizeof(dp));
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (a[i] == b[j]) {
                dp[i % 2][j] = dp[!(i % 2)][j - 1] + 1;
            } else {
                dp[i % 2][j] = max(dp[!(i % 2)][j], dp[i % 2][j - 1]);
            }
        }
    }
}
void lis() {
    for (int i = 1; i <= n; i++)
        b[i] = a[i];
    sort(b + 1, b + n + 1);
    lcs();
    cout << dp[n % 2][n] << endl;
}
//空间复杂度太大,重建有点难
int main() {
#ifdef LOCAL
    freopen("zz_in.txt", "r", stdin);
    freopen("zz_op.txt", "w", stdout);
#endif

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

#ifdef LOCAL
    printf("Time used = %.2f\n", (double) clock() / CLOCKS_PER_SEC);
#endif
    return 0;
}

2、一维数组dp(可回溯,时间复杂度O(n^2))

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <stack>
#include <vector>

using namespace std;

const int INF = 0x3f3f3f3f;
const int MAXN = 10005;
int a[MAXN];
int dp[MAXN];
int from[MAXN]; //重建最优解用
stack<int> rb;
int n, m, ans_index;
//递增子序列!=连续递增子序列
int lis() {
    int ans = 0;
    ans_index = 0;
    memset(dp, 0, sizeof(dp));
    memset(from, 0, sizeof(from));
    for (int i = 1; i <= n; i++) {
        int tmp = 0;
        for (int j = 1; j < i; j++) {
            if (a[i] > a[j] && dp[j] > tmp) {
                tmp = dp[j];
                from[i] = j;
            }
        }
        dp[i] = tmp + 1;
        if (dp[i] > ans) {
            ans = dp[i];
            ans_index = i;
        }
    }
    return ans;
}
void rebuild() {
    while (from[ans_index] != 0) {
        rb.push(a[ans_index]);
        ans_index = from[ans_index];
    }
    rb.push(a[ans_index]);
    while (!rb.empty()) {
        cout << rb.top() << " ";
        rb.pop(); //忘了pop导致死循环
    }
    cout << endl;
}
int main() {
#ifdef LOCAL
    freopen("zz_in.txt", "r", stdin);
    freopen("zz_op.txt", "w", stdout);
#endif
    while (cin >> n) {
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        cout << lis() << endl;
    }

#ifdef LOCAL
    printf("Time used = %.2f\n", (double) clock() / CLOCKS_PER_SEC);
#endif
    return 0;
}

3、最快的办法(O(nlogn))
不太好回溯

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <vector>

using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 10005;
int n, m;
int a[MAXN];
int b[MAXN];
// lower_bound()  upper_bound()使用方法
int lis() {
    memset(b, 0, sizeof(b));
    int len = 1;
    b[1] = a[1];
    for (int i = 2; i <= n; i++) {
        if (a[i] > b[len])
            b[++len] = a[i];
        else {
            int j = lower_bound(b + 1, b + len + 1, a[i]) - b;
            b[j] = a[i];
        }
    }
    return len;
}
int main() {
#ifdef LOCAL
    freopen("zz_in.txt", "r", stdin);
    freopen("zz_op.txt", "w", stdout);
#endif
    while (cin >> n) {
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        cout << lis() << endl;
    }
    // int e[] = {1, 2, 3, 5, 6, 7};
    // cout << e[lower_bound(e, e + 6, 5) - e] << endl; //5    第一个<=5的元素
    // cout << e[upper_bound(e, e + 6, 5) - e] << endl; //6    第一个>5的元素
    //之前upper_bound    lower_bound搞错了
    // upper_bound( first, last, & val)算法返回一个非递减序列[first, last)中第一个 大于 val的地址。
    // lower_bound( first, last, & val)算法返回一个非递减序列[first, last)中第一个 大于等于 值val的地址。
    // upper_bound( first, last, & val, greater())算法返回一个非递增序列[first, last)中第一个 小于 val的地址。
    // lower_bound( first, last, & val, greater())算法返回一个非递增序列[first, last)中第一个 小于等于 值val的地址。
    // int pos = lower_bound(age.rbegin(), age.rend(), a[i]) - age.rbegin();
    //rbegin rend反向迭代器(倒着查找)

#ifdef LOCAL
    printf("Time used = %.2f\n", (double) clock() / CLOCKS_PER_SEC);
#endif
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值