O(nlogn)求出最長不上升子序列的長度
1.實現方式
首先我們需要一個數組a,儲存從第1個到第n個導彈的高度
然後一個數組d(其實是個棧),儲存不上升序列
把a中的每個元素挨個加到d裡面:
(a中第i個元素為a[i],d長度為len,d中最後一個(也是最小的一個)為d[len])
如果a[i] <= d[len],說明a[i]可以接在d後面(而整個d還是有序的),那就簡單粗暴地把a[i]丟進d:
d[++len] = a[i]
如果a[i] > d[len],說明a[i]接不上
但是我們發揚瞎搞精神:接的上要接,接不上創造條件也要接!
強行把a[i]塞進去:
在d中找到第一個小於a[i]的數,把它踹了,用a[i]代替它!(為什麼正確在下面)
假設這個數是y,怎樣踹掉它呢?
很明顯,我們需要使用lower_bound和upper_bound來查詢
第一步,找一個聽起來無比正確的理由,比如它佔著位置不幹活啦,幹起活來還不如a[i]啦,naive啦,它too young啦,too simple啦......反正能騙過lower_bound和upper_bound就行
(lower_bound&&upper_bound:你當我們傻)(w1049:真聰明)
接下來,特別有正義感的lower_bound和upper_bound就會去把y給拎出來
第二步,考慮使用什麼
我們知道,要求的是最大不上升子序列長度,也就是如果兩個元素相等也是可以的
所以我們踹人就不用踹等於a[i]的了
結合上面,應該使用upper_bound(終於想起來它了)並且使用>作為比較器(這是個下降序列)
第三步,直接開搞
int p = upper_bound(d + 1, d + 1 + len, a[i], greater<int>()) - d;
d[p] = a[i];
成功把a[i]塞了進去
2.為什麼正確
顯然成立
如果y在末尾,由於y < a[i],所以y後面能接的不如a[i]多,y讓位給a[i]可以讓序列更長
如果y不在末尾,那y有生之年都不會再被用到了,直接踹了y就行,y咋樣,who care?
注意到lower_bound只能在有序序列中使用,此時d還有序嗎?
當然有序。(本文第一個句號)
假設y前一個y1,y後一個是y2,則
y1 > y > y2y1>y>y2
因為y是第一個小於a[i]的,所以
y1 > a[i]y1>a[i]
又因為
a[i] > y > y2a[i]>y>y2
所以
y1 >y1>a[i]> y2>y2
對比下原來的式子
y1 >y1>y> y2>y2
a[i]可以完美代替y,至於y以後咋辦,who care?
對於最長上升子序列,只需要把上面的過程通通換一下符號
可以用以下方法證明:
反之亦然同理,推論自然成立,略去過程QED,由上可知證畢(多麼美妙的證明)
實際上,d[i]d[i]的含義是:最大不上升子序列長度為ii時,最優的結尾元素。
3.程式碼:
for(int i=2;i<=n;i++)
if(d[len]>=a[i])d[++len]=a[i];
else {
int p=upper_bound(d+1,d+1+len,a[i],greater<int>())-d;
d[p]=a[i];
}
最後len就是要求的最大不上升子序列長度
但要注意的是,d中儲存的並不是最大不上升子序列!
原因如下:
即得易見平凡,仿照上例顯然,留作習題答案略,讀者自證不難
4.對樣例模擬:
在這裡推薦一下DevC++的偵錯程式(不用DevC++的當我沒說)
(還是不要推薦了)
1.我們把a[i]**(389)**加入d:
2.i=2,此時a[i](207)<=d[len](389),把a[2]加入d:
3.i=3,此時a[i](155)<=d[len](207),把a[3]加入d:
4.i=4,此時a[i](300)>d[len](155),不能直接加入,所以準備踹人
5.找出d中第一個小於a[i](300)的(即207),用a[i]換掉
6.i=5,此時a[i](299)>d[len](155),不能直接加入,所以準備踹人
7.找出d中第一個小於a[i](299)的(即155),用a[i]換掉
8.i=6,此時a[i](170)<=d[len](299),把a[6]加入d:
9.i=7,此時a[i](158)<=d[len](170),把a[7]加入d:
10.i=8,此時a[i](65)<=d[len](158),把a[8]加入d:
至此,得到最大不上升子序列長度len=6
by——w1049344862
洛谷
——END——