目錄php
求最長不降低/上升/降低/不上升/子序列的原理都是同樣的,只是有無等於號或者大於小於號的關係,懂了一個其他的就懂了,因此我這裏只拿不降低子序列來舉例子吧。ios
求最長不降低子序列的話,\(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; }
這纔是這篇文章的重點!!!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; }