昨天要写一道最长上升子序列的题,想起了自己曾经写过一篇,翻出来看了一下,只有一个感觉 ~~~ 满眼都是水
这篇算是上一篇的完善和追加.
最长上升子序列 -----最长不下降子序列
最长不上升子序列 ---- 最长下降子序列
最长上升子序列 和 最长不下降子序列
最长上升子序列的核心思想就是 追加 和 替换
有一个数组 a[],我们要在 a[] 中找到一个最长上升子序.
首先我们需要维护一个数组 lis ,这个数组用来保存 a[] 中的最长上升子序.
然后需要判断 a[] 中的每一个元素.
如果 a [ ] a[] a[] 中的元素 a [ i ] a[i] a[i] 大于当前 l i s lis lis 的最后一个元素 l i s [ c n t ] lis[cnt] lis[cnt],就将该元素追加在 l i s [ ] lis[] lis[] 后面,
否则就在 l i s lis lis 中找第一个大于等于 a [ i ] a[i] a[i] 的元素进行替换.
举个例子:
a [ ] = 2 , 1 , 3 , 5 , 6 , 4 a[] = {2,1,3,5,6,4} a[]=2,1,3,5,6,4;
l i s [ ] = 2 lis[] = {2} lis[]=2;
l i s [ 0 ] = a [ 0 ] lis[0] = a[0] lis[0]=a[0]
从第二个元素进行判断
当 i = 1 i = 1 i=1 时 -> l i s [ ] = 1 lis[] = 1 lis[]=1
当 i = 2 i = 2 i=2 时 -> l i s [ ] = 1 , 3 lis[] = 1,3 lis[]=1,3
当 i = 3 i = 3 i=3 时 -> l i s [ ] = 1 , 3 , 5 lis[] = 1,3,5 lis[]=1,3,5
当 i = 4 i = 4 i=4 时 -> l i s [ ] = 1 , 3 , 5 , 6 lis[] = 1,3,5,6 lis[]=1,3,5,6
当 i = 5 i = 5 i=5 时 -> l i s [ ] = 1 , 3 , 4 , 6 lis[] = 1,3,4,6 lis[]=1,3,4,6
因为每一次操作都是当前最长且最小的子序列,所以不会漏掉任何一个元素
最长不下降子序列 和 最长上升子序列类似
上升子序列 -> 当 i > 1 i > 1 i>1时,a[i-1] < a[i]
不下降序列 -> 当 i > 1 i > 1 i>1时,a[i- 1] <= a[i]
最长不下降子序列
如果 a [ ] a[] a[] 中的元素 a [ i ] a[i] a[i] 大于等于当前 l i s lis lis 的最后一个元素 l i s [ c n t ] lis[cnt] lis[cnt],就将该元素追加在 l i s [ ] lis[] lis[] 后面,
否则就在 l i s lis lis 中找第一个大于等于 a [ i ] a[i] 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 [ ] a[] a[] 中的元素 a [ i ] a[i] a[i] 小于当前 l i s lis lis 的最后一个元素 l i s [ c n t ] lis[cnt] lis[cnt],就将该元素追加在 l i s [ ] lis[] lis[] 后面,
否则就在 l i s lis lis 中找第一个小于等于 a [ i ] a[i] a[i] 的元素进行替换.
举个例子:
a [ ] = 4 , 7 , 5 , 3 , 6 , 2 a[] = {4,7,5,3,6,2} a[]=4,7,5,3,6,2;
l i s [ ] = 4 lis[] = {4} lis[]=4;
l i s [ 0 ] = a [ 0 ] lis[0] = a[0] lis[0]=a[0]
从第二个元素进行判断
当 i = 1 i = 1 i=1 时 -> l i s [ ] = 7 lis[] = 7 lis[]=7
当 i = 2 i = 2 i=2 时 -> l i s [ ] = 7 , 5 lis[] = 7,5 lis[]=7,5
当 i = 3 i = 3 i=3 时 -> l i s [ ] = 7 , 5 , 3 lis[] = 7,5,3 lis[]=7,5,3
当 i = 4 i = 4 i=4 时 -> l i s [ ] = 7 , 6 , 3 lis[] = 7,6,3 lis[]=7,6,3
当 i = 5 i = 5 i=5 时 -> l i s [ ] = 7 , 6 , 3 , 2 lis[] = 7,6,3,2 lis[]=7,6,3,2
因为每一次操作都是当前最长且最大的子序列,所以不会漏掉任何一个元素
最长不上升序列 和 最长下降序列类似
下降子序列 -> 当 i > 1 i > 1 i>1时,a[i-1] > a[i]
不上升序列 -> 当 i > 1 i > 1 i>1时,a[i- 1] >= a[i]
最长不下降子序列
如果 a [ ] a[] a[] 中的元素 a [ i ] a[i] a[i] 小于等于当前 l i s lis lis 的最后一个元素 l i s [ c n t ] lis[cnt] lis[cnt],就将该元素追加在 l i s [ ] lis[] lis[] 后面,
否则就在 l i s lis lis 中找第一个小于等于 a [ i ] a[i] 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;
}
最后附加一道例题,有兴趣的可以了解一下 导弹拦截