====接力dalao完成====
前文鏈接:(CSP-S RP++!)
對前文的一些補充:
首先清楚最長不下降子序列是一個遞增但是允許不同位元素相等的序列。而最長上升子序列則是一個單調遞增的序列。
而兩者都是子序列,所以子序列的長度一定小於等於原序列。且子序列在原序列的位置不一定連續。
這個O(nlogn)的算法使用的是貪心的思想。
為了幫助理解,請與以下代碼對比閱讀:
#include<iostream> using namespace std; int a[1000001],dp[1000001]; int ans; int n; int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; } for(int i=2;i<=n;i++) { for(int j=1;j<=i;j++) { if(a[j]<a[i]) dp[i]=max(dp[j]+1,dp[i]);//暴力從原序列中找比當前位置小的數,並不斷更新當前位置的最長序列值(比較好理解) //想不懂就。。。再想想吧// } ans=max(dp[i],ans); } cout<<ans<<endl; return 0; }
這個是O(n2)的暴力算法,大概思路就是每次選取一個終點,再暴力求出終點的最長序列值,再從答案中選取最大值。
而O(nlogn)的實現過程與這個正好相反。
因為每次暴力更新都會有很多不必要的比對,比如對於原序列
1 2 3 4 5
當前選定的終點是5,在上面的暴力程序中,對於位置5會與1,2,3,4各比較一次,然后得出最后答案。然而,因為這個隊列已經是單調遞增的,所以5只需要與前一位4比對一次就可以得出答案,從而省去前面3次無用的比對。
為了避免浪費時間,這里再開一個數組d來存儲已經找出的性價比最高的最長不下降子序列。
這里的“性價比”是指如果采用當前這個子序列作為既定的開頭,用於下面繼續比對,這樣得出的答案一定是最優的。
舉例來說:
對於原序列:
1 2 5 7 8 1 10
有下列子序列
a:1 5 7 8
b:1 2 5 8
稱b的性價比高於a,其原理是,對於b數組中相鄰兩個元素的差要小於a數組中的,而且最后一個元素要比a數組的小,此時稱b的性價比比a高。
而對於性價比更高的序列,再接着處理時,最終所得的結果是最優的。(有最優子結構)
而對於任意一個位置,若在原序列中的a[i]>d[len],其中len為d的長度,那么d[len++]=a[i];
這個的原理很簡單,但是當a[i]<d[len]時,應該怎么處理呢?
去尋找d數組中第一個第一個大於a[i]的數,讓a[i]與該數互換位置,得到性價比更高的序列,這次操作的原因已經在上文中闡述過。
又因為在上述操作過程中,d數組是一個不下降序列,所以可以用STL中的upper_bound來簡化過程。
(upper_bound在dalao的題解里已經有解釋,再復制一下)
首先介紹兩個STL,非常好用(用於解決這道題)
(球球你看看它,如果看不懂就先看算法再看它,超級省事的)
lower_bound與upper_bound
- 使用二分的思想
- (所以要求在一個有序的序列內(你樂意的話自己定義一種排序方式也行,但是要有序(不然你也不知道它會出來什么亂七八糟的)))
- (默認為升序)
- 復雜度大致為 log n
用法:lower_bound(a+1,a+n+1,x)
返回 a 數組內 第一個大於等於 x 的數的指針
令 P = lower_bound(a+1,a+n+1,x)- a,a [ p ] 則 為第一個大於等於 x 的數
(如果你會指針的話) * p = lower_bound(a+1,a+n+1,x)也是 第一個大於等於 x 的數
upper_bound 和它的用法差不多,除了返回的是第一個大於 x 的指針
(也就是求最大不下降子序列和最大上升子序列的差別)
若我們要求下降序列呢 ?
我們可以寫一個 cmp,或者使用 C++ 自帶的 greater<>() (都在STL里)
(和 sort 寫法差不多)(sort總該寫過的)
lower_bound(a,a+1,x,cmp) / lower_bound(a,a+1,x,greater<>());
一個小小的問題:
是怎樣保證d數組中的數在原數組中的下標也是遞增的呢?
這個問題是不用考慮的,因為最后決定答案的是len,並不是d數組或是數組內的元素決定的,d數組內只是存儲可行的最優解,用來優化答案。
若當前原數組中存在一條比當前d數組更長的最長不下降子序列,才會影響最后的答案長度。
這樣問題就不大了(看不懂可以模擬幾組數組來理解一下)
代碼:
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int a[100001],d[100001],n,len; int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; } d[1]=a[1]; len=1; for(int i=2;i<=n;i++) { if(a[i]>d[len]) d[++len]=a[i]; else { int j=upper_bound(d+1,d+len+1,a[i])-d; d[j]=a[i]; } for(int i=1;i<=n;i++) { cout<<d[i]<<" "; } cout<<endl;//這個是分段輸出,幫助理解 } cout<<len<<endl; return 0; }
無注釋的點這里:

#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int a[100001],d[100001],n,len; int main() { cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; } d[1]=a[1]; len=1; for(int i=2;i<=n;i++) { if(a[i]>d[len]) d[++len]=a[i]; else { int j=upper_bound(d+1,d+len+1,a[i])-d; d[j]=a[i]; } } cout<<len<<endl; return 0; }
應該就這樣了(看不懂我也。。自己搜吧)
dalao友情提供的例題:(這個hin經典) (這個超級妙)
掰掰
我寫完了@蒔蘿蘿
CSP-S RP++!