今天花了很長時間終於弄懂了這個演算法……畢竟找乙個好的講解真的太難了,所以勵志我要自己寫乙個好的講解qaq
這篇文章是在懂了這個問題n^2解決方案的基礎上學習。
解決的問題:給定乙個序列,求最長不下降子串行的長度(nlogn的演算法沒法求出具體的序列是什麼)
定義:a[1..n]為原始序列,d[k]表示長度為k的不下降子串行末尾元素的最小值,len表示當前已知的最長子序列的長度。
初始化:d[1]=a[1]; len=1; (0個元素的時候特判一下)
現在我們已知最長的不下降子串行長度為1,末尾元素的最小值為a[1],那麼我們讓i從2到n迴圈,依次求出前i個元素的最長不下降子串行的長度,迴圈的時候我們只需要維護好d這個陣列還有len就可以了。
關鍵問題就是怎麼維護?
可以看出我們是要用logn的複雜度維護的。實際上利用了d陣列的乙個性質:單調性。(長度更長了,d[k]的值是不會減小的)
考慮新進來乙個元素a[i]:
如果這個元素大於等於d[len],直接讓d[len+1]=a[i],然後len++。這個很好理解,當前最長的長度變成了len+1,而且d陣列也新增了乙個元素。
如果這個元素小於d[len]呢?說明它不能接在最後乙個後面了。那我們就看一下它該接在誰後面。
準確的說,並不是接在誰後面。而是替換掉誰。因為它接在前面的誰後面都是沒有意義的,再接也超不過最長的len,所以是替換掉別人。那麼替換掉誰呢?就是替換掉那個最該被它替換的那個。也就是在d陣列中第乙個大於它的。第乙個意味著前面的都小於等於它。假設第乙個大於它的是d[j],說明d[1..j-1]都小於等於它,那麼它完全可以接上d[j-1]然後生成乙個長度為j的不下降子串行,而且這個子串行比當前的d[j]這個子串行更有潛力(因為這個數比d[j]小)。所以就替換掉它就行了,也就是d[j]=a[i]。其實這個位置也是它唯一能夠替換的位置(前面的替了不滿足d[k]最小值的定義,後面替換了不滿足不下降序列)
至於第乙個大於它的怎麼找……stl upper_bound。每次複雜度logn。
至此,我們就神奇的解決了這個問題。按照這個思路,如果需要求嚴格遞增的子串行怎麼辦?
仍然考慮新進來乙個元素a[i]:
如果這個元素大於d[len],直接讓d[len+1]=a[i],然後len++。這個很好理解,當前最長的長度變成了len+1,而且d陣列也新增了乙個元素。
如果這個元素小於等於d[len]呢?說明它不能接在最後乙個後面了。那我們就看一下它該接在誰後面。
同樣的道理,只是換成lower_bound即可。每次複雜度logn。
--------2018.4.14更新--------
最長遞增子串行和最長不下降子串行的不同之處在於,d陣列的單調性更嚴格了:一定是單調遞增的。
可以用反證法來證明這一點:假設有d[i]=d[i+1],也就是長度為i+1的子串行最後一位最小是d[i+1],那假設這個子串行是a[1], a[2], ..., a[i+1],在這個子串行裡面,a[i]那這個性質有什麼意義呢?
仍然考慮新進來乙個元素a[i]:
如果這個元素大於d[len],直接讓d[len+1]=a[i],然後len++。這個很好理解,當前最長的長度變成了len+1,而且d陣列也新增了乙個元素。
如果這個元素等於d[len],那麼可以保證d[1..len-1]都是小於a[i]的(根據上面的證明),因此這個元素就沒有什麼意義了,直接忽略就好,因為它無法接在任何乙個元素d後面產生乙個更有優勢的子串行。
如果這個元素小於d[len],那麼就在d陣列中找到第乙個大於等於它的元素(這個元素必然存在,至少d[len]就是),把這個元素替換成a[i]即可。
實際上可以發現,小於等於的時候可以統一按照lower_bound替換的方式來處理。
這樣做肯定是對的,而鄺斌的模板上實際上是求的最長不下降子串行,而不是最長上公升子串行。不信可以測試一下"1 2 3 2 3 2"這個樣例。
切記,不要迷信權威,學會自己思考。
------------------------------------
下面是最長不下降子串行的**,注釋裡面說明了如何改成最長上公升子串行。
//最長不下降子串行nlogn song
#include
#include
using
namespace
std;
int a[40005
];int d[40005
];int
main()
d[1]=a[1]; //
初始化
int len=1
;
for (int i=2;i<=n;i++)
}printf(
"%d\n
",len);
return0;
}
想了一晚上這個問題終於想通了。前面說的「最該替換的位置」實際上不是很精確,那個位置替換掉是它唯一能夠替換的位置,之所以要替換,就是為了維護d這個陣列,讓它始終滿足最初的定義。
nlogn複雜度的最長上公升子串行還有樹狀陣列的寫法,可以參考我的另一篇文章:
nlogn 最長不下降子串行
o nlogn 的演算法關鍵是它建立了乙個陣列temp,temp i 表示長度為i的不下降序列中結尾元素的最小值,用top表示陣列目前的長度,演算法完成後top的值即為最長不下降子串行的長度。設當前的以求出的長度為top,則判斷num i 和temp top 1.如果num i temp top 即...
nlogn 最長不下降子串行
o nlogn 的演算法關鍵是它建立了乙個陣列temp,temp i 表示長度為i的不下降序列中結尾元素的最小值,用top表示陣列目前的長度,演算法完成後top的值即為最長不下降子串行的長度。設當前的以求出的長度為top,則判斷num i 和temp top 1.如果num i temp top 即...
最長不下降子串行
a1 t0 an a an 1 2 b an c d n 1 求該序列最長不下降子串行長度 n不是很大顯然可以暴力。n很大呢?那就不斷減迴圈節長度直至減到乙個閾值內,再暴力。正確性顯然,只要閾值不要設太小。include include include define fo i,a,b for i a...