浅谈O(nlogn)级别求LCS与LIS问题

【模板】最长公共子序列 - 洛谷

这个题是LCS转换成LIS问题,首先应该将a数组中的数按照1~n的全排列进行重新定义,然后替换b数组中的数,根据a数组重新定义的数

如a:1 4 3 5 2 -> a:1 2 3 4 5

    b:2 3 5 4 1 -> b:5 3 4 2 1

这样就成功把求LCS问题转化成求解LIS问题,就可以开心的用二分求解十万数量的题目了,第一次觉得l+1<r姿势的二分不好使了QAQ

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<int> a(n + 1), b(n + 1);
    unordered_map<int, int> mp;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        mp[a[i]] = i;
    }
    for (int i = 1; i <= n; i++) {
        cin >> b[i];
        b[i] = mp[b[i]];
    }
    int t = 0;
    vector<int> c(n + 1);
    c[++t] = b[1];
    for (int i = 2; i <= n; i++) {
        if (b[i] > c[t]) {
            c[++t] = b[i];
        }
        else {
            int l = 1, r = t;
            while (l < r) {
                int mid = l + r >> 1;
                if (b[i] > c[mid]) {
                    l = mid + 1;
                }
                else {
                    r = mid;
                }
            }
            c[l] = b[i];
        }
    }
    cout << t << '\n';
    return 0;
}

Problem - 1172

题意很简单,从T1到Ti严格递增,从Ti到Tm严格递减,因为要枚举i的位置,所以朴素的DP方式的求法不满足需求,所以用二分法优化求解,对于数据比较大的二分才是正解,(不仅是数据比较大的时候,DP才是歪门邪道/狗头

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int a[1010], b[1010], c[1010];
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int t = 0;
        b[++t] = a[1];
        for (int j = 2; j <= i; j++) {
            if (a[j] > b[t]) {
                b[++t] = a[j];
            }
            else {
                int l = 1, r = t;
                while (l <= r) {
                    int mid = l + r >> 1;
                    if (a[j] > b[mid]) {
                        l = mid + 1;
                    }
                    else {
                        r = mid - 1;
                    }
                }
                b[l] = a[j];
            }
        }
        int z = 0;
        c[++z] = a[i];
        for (int j = i + 1; j <= n; j++) {
            if (a[j] < c[z]) {
                c[++z] = a[j];
            }
            else {
                int l = 1, r = z;
                while (l <= r) {
                    int mid = l + r >> 1;
                    if (a[j] < c[mid]) {
                        l = mid + 1;
                    }
                    else {
                        r = mid - 1;
                    }
                }
                c[l] = a[j];
            }
        }
        ans = max(ans, z + t - 1);
    }
    cout << ans << '\n';
    return 0;
}

[NOIP1999 普及组] 导弹拦截 - 洛谷

O(nlogn)求解最长不下降子序列和最长上升子序列

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int n;
	vector<int> a;
	int x;
	a.push_back(0);
	while (cin >> x) {
		a.push_back(x);
	}
	int len = a.size() - 1;
	int t = 0, t1 = 0;
	vector<int> dp1(len + 2), dp2(len + 2);
	dp1[++t] = a[1];
	dp2[++t1] = a[1];
	for (int i = 2; i <= len; i++) {
		if (a[i] <= dp1[t]) {
			dp1[++t] = a[i];
		} else {
			int p = upper_bound(dp1.begin() + 1, dp1.begin() + 1 + t, a[i], greater<int>()) - dp1.begin();
			dp1[p] = a[i];
		}
		if (a[i] > dp2[t1]) {
			dp2[++t1] = a[i];
		} else {
			int p = lower_bound(dp2.begin() + 1, dp2.begin() + 1 + t1, a[i]) - dp2.begin();
			dp2[p] = a[i];
		}
	}
	cout << t << '\n';
	cout << t1 << '\n';
	return 0;
}

[NOIP2004 提高组] 合唱队形 - 洛谷

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int n;
	cin >> n;
	vector<int> a(n + 1);
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		int t = 0;
		vector<int> dp(n + 1);
		dp[++t] = a[i];
		for (int j = i - 1; j >= 1; j--) {
			if (a[j] < dp[t]) {
				dp[++t] = a[j];
			} else {
				*lower_bound(dp.begin() + 1, dp.begin() + 1 + t, a[j], greater<int>()) = a[j];
			}
		}
		int z = 0;
		vector<int> dp1(n + 1);
		dp1[++z] = a[i];
		for (int j = i + 1; j <= n; j++) {
			if (a[j] < dp1[z]) {
				dp1[++z] = a[j];
			} else {
				*lower_bound(dp1.begin() + 1, dp1.begin() + 1 + z, a[j], greater<int>()) = a[j];
			}
		}
		ans = max(ans, t + z);
	}
	cout << n - ans + 1 << '\n';
	return 0;
}

木棍加工 - 洛谷

AC代码:

#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int n;
	cin >> n;
	vector<pair<int, int>> a(n + 1);
	for (int i = 1; i <= n; i++) {
		cin >> a[i].first >> a[i].second;
	}
	sort(a.begin() + 1, a.end(), [&](pair<int, int> a, pair<int, int> b) {
		if (a.first != b.first) {
			return a.first > b.first;
		}
		return a.second > b.second;
	});
	vector<int> f(n + 1);
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		if (a[i].second > f[ans]) {
			f[++ans] = a[i].second;
		} else {
			*lower_bound(f.begin() + 1, f.begin() + 1 + ans, a[i].second) = a[i].second;
		}
	}
	cout << ans << '\n';
	return 0;
}

注意

关于lower_bound()和upper_bound(),如果只传三个参数,则表示在升序序列中分别查找第一个大于等于和第一个大于的元素,那如果想用这个在降序序列中,需要再传一个cmp参数,即使用greater<int>()函数,虽然lower_bound()和upper_bound()本身的功能并没有改变,但针对的序列发生了改变,如果加上greater<int>()函数,则表示在降序序列中分别查找第一个小于等于和第一个小于的元素

有个没有证明的小芝士(对错不一定):

在严格要求单增或单减时用lower_bound(),在不严格要求单增或单减用upper_bound().

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值