最长上升子序列 和 最长下降子序列

昨天要写一道最长上升子序列的题,想起了自己曾经写过一篇,翻出来看了一下,只有一个感觉 ~~~ 满眼都是水
这篇算是上一篇的完善和追加.

最长上升子序列 -----最长不下降子序列
最长不上升子序列 ---- 最长下降子序列

最长上升子序列 和 最长不下降子序列

最长上升子序列的核心思想就是 追加替换

有一个数组 a[],我们要在 a[] 中找到一个最长上升子序.
首先我们需要维护一个数组 lis ,这个数组用来保存 a[] 中的最长上升子序.
然后需要判断 a[] 中的每一个元素.

如果 a [ ] a[] 中的元素 a [ i ] a[i] 大于当前 l i s lis 的最后一个元素 l i s [ c n t ] lis[cnt] ,就将该元素追加在 l i s [ ] lis[] 后面,
否则就在 l i s lis 中找第一个大于等于 a [ i ] a[i] 的元素进行替换.
举个例子:
a [ ] = 2 , 1 , 3 , 5 , 6 , 4 a[] = {2,1,3,5,6,4} ;
l i s [ ] = 2 lis[] = {2} ;
l i s [ 0 ] = a [ 0 ] lis[0] = a[0]
从第二个元素进行判断
i = 1 i = 1 时 -> l i s [ ] = 1 lis[] = 1
i = 2 i = 2 时 -> l i s [ ] = 1 , 3 lis[] = 1,3
i = 3 i = 3 时 -> l i s [ ] = 1 , 3 , 5 lis[] = 1,3,5
i = 4 i = 4 时 -> l i s [ ] = 1 , 3 , 5 , 6 lis[] = 1,3,5,6
i = 5 i = 5 时 -> l i s [ ] = 1 , 3 , 4 , 6 lis[] = 1,3,4,6

因为每一次操作都是当前最长且最小的子序列,所以不会漏掉任何一个元素

最长不下降子序列 和 最长上升子序列类似

上升子序列 -> 当 i > 1 i > 1 时,a[i-1] < a[i]
不下降序列 -> 当 i > 1 i > 1 时,a[i- 1] <= a[i]

最长不下降子序列

如果 a [ ] a[] 中的元素 a [ i ] a[i] 大于等于当前 l i s lis 的最后一个元素 l i s [ c n t ] lis[cnt] ,就将该元素追加在 l i s [ ] lis[] 后面,
否则就在 l i s lis 中找第一个大于等于 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 [ i ] a[i] 小于当前 l i s lis 的最后一个元素 l i s [ c n t ] lis[cnt] ,就将该元素追加在 l i s [ ] lis[] 后面,
否则就在 l i s lis 中找第一个小于等于 a [ i ] a[i] 的元素进行替换.
举个例子:
a [ ] = 4 , 7 , 5 , 3 , 6 , 2 a[] = {4,7,5,3,6,2} ;
l i s [ ] = 4 lis[] = {4} ;
l i s [ 0 ] = a [ 0 ] lis[0] = a[0]
从第二个元素进行判断
i = 1 i = 1 时 -> l i s [ ] = 7 lis[] = 7
i = 2 i = 2 时 -> l i s [ ] = 7 , 5 lis[] = 7,5
i = 3 i = 3 时 -> l i s [ ] = 7 , 5 , 3 lis[] = 7,5,3
i = 4 i = 4 时 -> l i s [ ] = 7 , 6 , 3 lis[] = 7,6,3
i = 5 i = 5 时 -> l i s [ ] = 7 , 6 , 3 , 2 lis[] = 7,6,3,2

因为每一次操作都是当前最长且最大的子序列,所以不会漏掉任何一个元素

最长不上升序列 和 最长下降序列类似

下降子序列 -> 当 i > 1 i > 1 时,a[i-1] > a[i]
不上升序列 -> 当 i > 1 i > 1 时,a[i- 1] >= a[i]

最长不下降子序列

如果 a [ ] a[] 中的元素 a [ i ] a[i] 小于等于当前 l i s lis 的最后一个元素 l i s [ c n t ] lis[cnt] ,就将该元素追加在 l i s [ ] lis[] 后面,
否则就在 l i s lis 中找第一个小于等于 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;
}

最后附加一道例题,有兴趣的可以了解一下 导弹拦截

猜你喜欢

转载自blog.csdn.net/qq_43701555/article/details/101435964