昨天要写一道最长上升子序列的题,想起了自己曾经写过一篇,翻出来看了一下,只有一个感觉 ~~~ 满眼都是水
这篇算是上一篇的完善和追加.
最长上升子序列 -----最长不下降子序列
最长不上升子序列 ---- 最长下降子序列
最长上升子序列 和 最长不下降子序列
最长上升子序列的核心思想就是 追加 和 替换
有一个数组 a[],我们要在 a[] 中找到一个最长上升子序.
首先我们需要维护一个数组 lis ,这个数组用来保存 a[] 中的最长上升子序.
然后需要判断 a[] 中的每一个元素.
如果
中的元素
大于当前
的最后一个元素
,就将该元素追加在
后面,
否则就在
中找第一个大于等于
的元素进行替换.
举个例子:
;
;
从第二个元素进行判断
当
时 ->
当
时 ->
当
时 ->
当
时 ->
当
时 ->
因为每一次操作都是当前最长且最小的子序列,所以不会漏掉任何一个元素
最长不下降子序列 和 最长上升子序列类似
上升子序列 -> 当
时,a[i-1] < a[i]
不下降序列 -> 当
时,a[i- 1] <= a[i]
最长不下降子序列
如果
中的元素
大于等于当前
的最后一个元素
,就将该元素追加在
后面,
否则就在
中找第一个大于等于
的元素进行替换.
#include<cstdio>
#include<cstring>
#include<algorithm>
#include <iostream>
using namespace std;
const int MAX = 1e5 + 10;
int a[MAX];
int lis[MAX]; //用来存 上升 序列
int main()
{
int n;
cin >> n;
memset(a,0,sizeof(a));
memset(lis,0,sizeof(lis));
for(int i = 1;i <= n;i++) cin >> a[i];
int cnt = 1;
lis[cnt] = a[1]; //将 第一个数 存进 s 数组
// 因为 每操作 一次 的结果都是 最长 且 最小 的子序列,所以 是不会漏掉 任何一个数的
for(int i = 2;i <= n;i++) // 第一个数已经判断过
{
if(a[i] > lis[cnt]) //如果a[i] > lis 的最后一个数字,就将 a[i] 存进 lis 数组
{ //如果要求 最长 不下降子序 则 将 这里 > 换成 >= 即可
cnt++;
lis[cnt] = a[i];
}
else
{
int temp = lower_bound(lis + 1,lis + cnt + 1,a[i]) - lis;// 在lis中 找到 第一个大于等于a[i]的 下标
lis[temp] = a[i]; // 将 找到的这个数 替换为 a[i]
}
//所以最后 lis 数组存放的也就是 最长 且 最小 的上升子序列
}
cout << cnt << endl;
return 0;
}
最长下降子序列 和 最长不上升子序列
最长下降子序列的核心思想也是 追加 和 替换
有一个数组 a[],我们要在 a[] 中找到一个最长下降子序.
首先我们需要维护一个数组 lds[] ,这个数组用来保存 a[] 中的最长下降子序.
然后需要判断 a[] 中的每一个元素.
如果
中的元素
小于当前
的最后一个元素
,就将该元素追加在
后面,
否则就在
中找第一个小于等于
的元素进行替换.
举个例子:
;
;
从第二个元素进行判断
当
时 ->
当
时 ->
当
时 ->
当
时 ->
当
时 ->
因为每一次操作都是当前最长且最大的子序列,所以不会漏掉任何一个元素
最长不上升序列 和 最长下降序列类似
下降子序列 -> 当
时,a[i-1] > a[i]
不上升序列 -> 当
时,a[i- 1] >= a[i]
最长不下降子序列
如果
中的元素
小于等于当前
的最后一个元素
,就将该元素追加在
后面,
否则就在
中找第一个小于等于
的元素进行替换.
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAX = 1e6 + 10;
int a[MAX];
int lds[MAX];
int found(int *a,int size,int x) // 用二分 在 lds 中找到第一个 小于等于 a[i] 的值
{
int l = 1,r = size;
while(l != r)
{
int mid = (r + l) >> 1;
if(x > a[mid]) r = mid;
else l = mid + 1;
}
return l;
}
int main()
{
int n = 1;
while(~scanf("%d",&a[n])) n++;
int cnt = 1;
lds[cnt] = a[1];
// 最长下降子序
for(int i = 2;i < n;i++)
{
//将 < 替换为 <= 即为 不上升子序
if(a[i] < lds[cnt])
{
cnt++;
lds[cnt] = a[i];
}
else lds[found(lds,cnt + 1,a[i])] = a[i];
}
cout << cnt << endl;
return 0;
}
最后附加一道例题,有兴趣的可以了解一下 导弹拦截