最長不下降子串行nlogn演算法詳解

2022-04-21 21:22:18 字數 2741 閱讀 8008

今天花了很長時間終於弄懂了這個演算法……畢竟找乙個好的講解真的太難了,所以勵志我要自己寫乙個好的講解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...