dp题目的优化
前言
今天做最长连续自序列的时候,发现有的时候dp:$O\left ( n^{2} \right )$的复杂度会超时,然而我们可以通过优化把时间复杂度降低为$O\left ( n\ast log_2 n \right )$,这个过程主要用到了二分和贪心的思想。优化过程如下:参考博客
$dp\left [ i \right ]$表示长度为i的上升子序列的最后一个元素的最小值。 然后考虑如何转移:
现在我们有一个新元素a,对比dp的最后一个元素。如果$a > dp\left [ len \right ]$ ,那么可以直接把a“接到”$dp\left [ len \right ]$的后面, 形成更长的上升子序列。即:$dp\left [ ++len \right ]= a$;
否则,找到dp中第一个大于等于a的元素,用a替换它 。这样替换后既保证仍形成上升子序列,又使得该上升子序列的最后元素更小。
但是这样做,无法记录路径。
例题
P1020 [NOIP1999 普及组] 导弹拦截
本题只需要找到最长非递增子序列和最长递增子序列即可,其中用到了lower_bound 和 upper_bound函数二分查找。这两个函数只能在升序序列中使用,所以在递减序列中查找时,需要修改一个小细节upper_bound(dp + 1, dp + sum + 1, a[i], greater<int>()) - dp 即可。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 20;
const int inf = 0x3f3f3f3f;
int a[maxn];
int dp[maxn];
int cnt = 0, tot = 0, sum = 0;
//O(nlogn)
int main() {
int num;
while (~scanf("%d", &num)) a[++ cnt] = num;
//最长非递增子序列
memset(dp, inf, sizeof(dp));
for (int i = 1; i <= cnt; i ++) {
if (a[i] <= dp[sum]) {
dp[++ sum] = a[i];
} else {
//只有在升序中才可以使用lower_bound 和 upper_bound
int t = upper_bound(dp + 1, dp + sum + 1, a[i], greater<int>()) - dp;
dp[t] = a[i];
}
}
cout << sum << "\n";
//最长上升子序列
memset(dp, 0, sizeof(dp));
for (int i = 1; i <= cnt; i ++) {
if (a[i] > dp[tot]) dp[++ tot] = a[i];
else {
int t = lower_bound(dp + 1, dp + 1 + tot, a[i]) - dp;
dp[t] = a[i];
}
}
cout << tot << "\n";
return 0;
}
#504. 连续子序列
最长上升子序列的翻版,多了一个要求,就是子序列中的数字要连续,即相邻两个数的差值为1。由于子序列要求是连续的,所以每一个状态只能由比当前数小1的状态得来,根本不用暴搜,只要询问前一个值即可。a的范围是1e9,直接开数组空间会炸,用一个map就可以解决了。 参考博客
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define TLE std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
const int maxn = 2e5 + 10;
int a[maxn];
map<int, int> f;
int main() {
TLE;
int n;
cin >> n;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
int maxx = 0, flag ;
for(int i = 1; i <= n; i ++) {
f[a[i]] = f[a[i] - 1] + 1;
if(f[a[i]] > maxx) {
maxx = f[a[i]];
flag = a[i];
}
else if(f[a[i]] == maxx && a[i] < flag) flag = a[i];
}
cout << maxx << "\n";
for(int i = flag - maxx + 1; i <= flag; i ++) {
cout << i << " ";
}
cout << "\n";
return 0;
}

浙公网安备 33010602011771号