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

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

說在前面的話

求最長不降低/上升/降低/不上升/子序列的原理都是同樣的,只是有無等於號或者大於小於號的關係,懂了一個其他的就懂了,因此我這裏只拿不降低子序列來舉例子吧。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;
}
相關文章
相關標籤/搜索