求最长不降低/上升/降低/不上升子序列

求最长不降低/上升/降低/不上升子序列

说在前面的话

求最长不降低/上升/降低/不上升/子序列的原理都是同样的,只是有无等于号或者大于小于号的关系,懂了一个其他的就懂了,因此我这里只拿不降低子序列来举例子吧。ios

\(n^2\)的作法

原理

求最长不降低子序列的话,\(i\)位置应该和\(i\)位置以前的位置里面小于等于\(i\)位置上的值且最长不降低子序列最长的那个,因此能够枚举\(i\)位置以前的每个点,而后按照上面说的选取就行了。数组

代码

题面详见spa

#include<iostream>
#include<cstdio>

using namespace std;
const int Max = 201;
int n;
int a[Max],f[Max],c[Max];
int Ans[Max];

int main()
{
    scanf("%d",&n); 
    for(int i = 1;i <= n;++ i) 
        scanf("%d",&a[i]),f[i] = 1;//输入数据将以每个数结尾的最长不降低子序列标记为1 
    for(int i = 1;i <= n;++ i)
        for(int j = 1;j < i;++ j)
            if(a[i] >= a[j])//知足最长不降低 , 也就是大于等于 
                if(f[i] < f[j] + 1)
                    f[i] = f[j] + 1,c[i] = j;//记录 
    int ans = 0,qwq = 1;
    for(int i = 1;i <= n;++ i)
        if(f[i] > ans)//找最大的 
            ans = f[i],qwq = i;//记录最大的所在的下标 
    printf("max=%d\n",ans);
    int aaa = ans;
    while(aaa --)//到这找回去 
    {
        Ans[aaa + 1] = a[qwq];//用数组储存 
        qwq = c[qwq];
    }
    for(int i = 1;i <= ans;++ i)//正着输出 
        cout<<Ans[i]<<" ";
    return 0;
}

nlogn的作法

这才是这篇文章的重点!!!code

原理

开一个数组(注意是额外开一个数组,不是存储读入数字串的数组),第一个数理所固然的放在这个数组的第一个位置上面,若是进来的下一个数是符合不降低条件的,那么久放在他的后面,否则就在这个序列里面找到第一个比他大的数替换掉。get

为何要这样呢?
看完上面应该是有一种疑惑,这样替换的话会不会影响后面的呢?答案固然是否认的。
来讲一下理由:
若是当前数组中放了1,3,5这三个数,下一个数是2,若是放在了第一个比他大的数的位置上,那就会放在3上面,这个时候数组中存储的数据就成了1,2,5,而后当下一个数进来的时候是和5比较是否是符合不降低的条件,因此把2替换在3的位置上面并无影响。
具体说一下:就是若是这个数替换的那个数以后还有数,必然不会影响以后一个数拿过来以后的第一项操做,也就是先比较一下放在最后面是否是会符合不降低,那若是后面没有数了,他就是最后一个的话,用这个小的替换了前面的大的,也必然会使结果更优,因此没有问题。it

代码

//最长不降低子序列nlogn  Song 

#include<cstdio>
#include<algorithm>
using namespace std;

int a[40005];
int d[40005];

int main()
{
    int n;
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    if (n==0)  //0个元素特判一下 
    {
        printf("0\n");
        return 0;
    }
    d[1]=a[1];  //初始化 
    int len=1;
    for (int i=2;i<=n;i++)
    {
        if (a[i]>=d[len]) d[++len]=a[i];  //若是能够接在len后面就接上,若是是最长上升子序列,这里变成> 
        else  //不然就找一个最该替换的替换掉 
        {
            int j=upper_bound(d+1,d+len+1,a[i])-d;  //找到第一个大于它的d的下标,若是是最长上升子序列,这里变成lower_bound 
            d[j]=a[i]; 
        }
    }
    printf("%d\n",len);    
    return 0;
}
相关文章
相关标签/搜索