模板1(单纯dp做法)
时间复杂度n^2
//for (int i = 1; i <= n; i++)
//{
// cin >> a[i];
// dp[i] = 1;
// for (int j = 1; j <= i - 1; j++)
// {
// if (a[i] > a[j])
// {
// dp[i] = max(dp[i], dp[j] + 1);
// }
// }
// length = max(length, dp[i]);
//}复杂度n^2的算法
不建议使用,但是好理解
模板2(二分dp)
O(nlogn)
int n,num[100100],lis[100100],len=0;
cin>>n;
memset(lis,0,sizeof(lis));
for(int i=0;i<n;i++)
{
scanf("%d",&num[i]);
}
lis[0] = num[0];
for(int i=1;i<n;i++)
{
if(num[i] > lis[len])
lis[++len] = num[i];
else
{
int pos = lower_bound(lis,lis+len,num[i]) - lis;
lis[pos] = num[i];
}
}
printf("%d\n",len+1);
模板二
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> num(n + 1), lis;
for (int i = 1; i <= n; i++) {
cin >> num[i];
}
for (int i = 1; i <= n; i++) {
if (lis.empty() || num[i] > lis.back()) {
lis.push_back(num[i]);
} else {
auto it = lower_bound(lis.begin(), lis.end(), num[i]);
*it = num[i];
}
}
cout << lis.size() << endl;
return 0;
}
第二个时间复杂度更低
这个代码仍然使用了 lower_bound
,但是只用于在 lis
数组中定位插入点,而不是在每次循环中都进行查找。这样可以减少查找的次数,提高性能。此外,使用 vector
而不是数组可以提供动态扩展和更高效的内存管理。
用于求解最长递增子序列(Longest Increasing Subsequence,简称LIS)的长度。在这段代码中,使用了二分查找(lower_bound
)来优化LIS的求解过程。
对于每个给定的数字 num[i]
,如果它大于当前LIS的最后一个元素 lis[len]
,则直接将其添加到LIS的末尾。如果它不大于,则使用二分查找在LIS中找到第一个大于等于 num[i]
的位置,并将该位置的元素替换为 num[i]
。这样做的目的是为了保持LIS尽可能小,从而在后续的元素中有更多的机会扩展这个序列。
二分查找的时间复杂度是 O(logn),其中 𝑛 是LIS的长度。在最坏的情况下,对于每个元素,我们都需要执行一次二分查找。因此,对于 n
个元素,总的时间复杂度是 O(nlogn)。
然而,由于LIS的长度不会超过 n
,所以二分查找的 n
实际上应该是LIS的长度,即 len
。但是,由于每次二分查找可能在LIS的不同位置进行,所以总的时间复杂度仍然是O(nlogn),这里的 𝑛是输入数组的长度。