这个题是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; }
题意很简单,从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; }
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; }
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().
浅谈O(nlogn)级别求LCS与LIS问题
于 2022-08-25 19:38:22 首次发布