最长上升子序列 两种写法
二 贪心 O nlogn;
解题思路
利用贪心 + 二分查找的思想。维护一个数组 q,其中 q[i] 表示长度为 i + 1 的最长上升子序列的末尾元素的最小值。
遍历数列:对输入数列中的每个元素 a[i] 进行处理。
二分查找:在 q 数组中通过二分查找找到第一个大于等于 a[i] 的位置。如果找到的位置 r 对应的 q[r] 大于 a[i],说明可以用 a[i] 替换 q[r],使得长度为 r + 1 的上升子序列末尾元素更小,更有利于后续构造更长的上升子序列;如果 r 等于当前记录的最长上升子序列长度 len,则说明找到了一个更长的上升子序列,len 自增。
更新数组:将 a[i] 赋值给 q[r],完成对 q 数组的更新。
获取结果:遍历完整个数列后,len 的值就是最长上升子序列的长度。
#include<bits/stdc++.h>
using namespace std;
const int N=100100;
int n;
int a[N];
int q[N];//距离为len的
int main()
{
cin>>n;
for(int i=0;i<n;i++) cin>>a[i];
q[0]=-2e9;
int len=0;
for(int i=0;i<n;i++)
{
int l=0,r=len;
while(l<r)
{
int mid=l+r+1>>1;
if(q[mid]<a[i])
{
l=mid;
}
else r=mid-1;
}
// cout<<r<<endl;
len=max(len,r+1);
q[r+1]=a[i];
// cout<<len<<" "<<q[len]<<endl;
}
cout<<len;
return 0;
}
// 最长上升子序列 第二种写法
int q[N] // 序列
int g[N] // 记录最长的最后一个值
int cnt=0;
for(int i=0;i<n;i++)
{
int k=0;
while(k<cnt && g[k]<q[i]) k++;
g[k]=q[i];
if(k>=cnt) cnt++;
}
cout<<cnt;
最长拦截系统
#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;
const int N=1e5+10; // 修正数组大小
int f[N], q[N], a[N], cnt=0;
void solve()
{
// 输入数据,以 EOF 结束(按 Ctrl+Z 或 Ctrl+D)
while(cin>>a[++cnt]);
cnt--; // 去掉最后的无效数据
// 计算最长非递增子序列(LNDS)
int len=0;
f[0] = INT_MAX; // 初始化哨兵
for(int i=1; i<=cnt; i++)
{
int l=0, r=len;
while(l < r)
{
int mid = (l + r + 1) >> 1;
if(f[mid] >= a[i]) l = mid;
else r = mid - 1;
}
len = max(len, r + 1);
f[r + 1] = a[i];
}
cout << len << "\n"; // 换行
// 计算最长递增子序列(LIS)
len=0;
q[0] = INT_MIN; // 初始化哨兵
for(int i=1; i<=cnt; i++)
{
int l=0, r=len;
while(l < r)
{
int mid = (l + r + 1) >> 1;
if(q[mid] < a[i]) l = mid;
else r = mid - 1;
}
len = max(len, r + 1);
q[r + 1] = a[i];
}
cout << len << "\n"; // 换行
}
int main()
{
ios;
int _=1;
while(_--)
{
solve();
}
return 0;
}
浙公网安备 33010602011771号