lis最长上升子序列o(nlogn)优化
LIS的暴力算法
我們知道,LIS(最長(zhǎng)上升子序列,最長(zhǎng)下降子序列,最長(zhǎng)不上升子序列,最長(zhǎng)不下降子序列)如果按照最初得方法做,我們?cè)O(shè)置的狀態(tài)f[i]表示i結(jié)尾的最長(zhǎng)LIS的長(zhǎng)度,在枚舉每一個(gè)數(shù)的時(shí)候都要向前找一個(gè)數(shù)字,那么這種方法是O(n^2).(具體講解看這里)
優(yōu)化后的LIS
如果N≤700000呢?O(n^2)的算法顯然是不符合要求的,我們可以考慮優(yōu)化LIS的DP
我們以最長(zhǎng)上升子序列為例:
我們?cè)O(shè)f[i]為長(zhǎng)度為i的最長(zhǎng)上升子序列結(jié)尾的最小個(gè)數(shù).
那么,我們?cè)诿杜e新的數(shù)字a[i]的時(shí)候,如果a[i]要比枚舉的f[j]大,則說明以a[i]結(jié)尾必然能夠形成長(zhǎng)度為j+1的最長(zhǎng)上升子序列.我們可以選擇來模擬這個(gè)過程.
設(shè)這串序列為{2,1,6,5,8,0,1,5,10}
1.因?yàn)闆]有數(shù)字,f[]={2}
2.因?yàn)?比2小,1無法接在任何數(shù)字后面,所以替代2,f[]={1}
3.因?yàn)?比任何一個(gè)數(shù)都要大,所以接在最前面,f[]={1,6}
4.5可以接在1后面并且比6要小,以此代替6,f[]={1,5}
5.因?yàn)?比任何數(shù)要大,接在末尾,f[]={1,5,8}
6.0比任何數(shù)都要小,所以f[]={0,5,8}
7.1可以接在0后面,所以f[]={0,1,8}
8.5可以接在8后面,所以f[]={0,1,5}
9.10可以接在5后面,所以f[]={0,1,5,10}
因?yàn)閒[4]不為0,說明可以構(gòu)成長(zhǎng)度為4的最長(zhǎng)上升子序列,故答案為4
為什么我們要在新的數(shù)字要是f[]數(shù)組內(nèi)的原有值最小?因?yàn)檫@樣更可以讓后面的數(shù)字構(gòu)成最長(zhǎng)上升組序列.因此我們需要在這個(gè)序列中查找,找到一個(gè)位置使得前面的數(shù)比它小并且后面的數(shù)字大于等于它,使得它能夠覆蓋這個(gè)數(shù)字.那么,這個(gè)算法的時(shí)間復(fù)雜度仍然后O(n^2),顯然無法優(yōu)化.
不難發(fā)現(xiàn),這個(gè)序列是單調(diào)遞增的,那么我們便可以選擇在查找的時(shí)候去優(yōu)化,在單調(diào)遞增的序列中我們可以需用二分查找去尋找那個(gè)可以覆蓋的位置,而二分查找的時(shí)間復(fù)雜度是O(lgn),再加上枚舉1-n的時(shí)間復(fù)雜度,故總復(fù)雜度為:O(n)*O(logn)=O(nlogn),顯然可以做到.
再重新說一下大致思路:
1.枚舉a[1...n]這些數(shù)字
2.如果這個(gè)數(shù)組比f數(shù)組末尾的數(shù)子要大,說明不能覆蓋任何一個(gè)數(shù)字,則放在最后
3.否則,用二分查找進(jìn)行查詢位置并進(jìn)行覆蓋.
同理,另外3中LIS也可以實(shí)現(xiàn).
有一個(gè)需要注意的地方,那就是:如果做上升的和不下降的LISf[0]需要賦值為-∞,否則賦值為+∞,我在這里用pow(10,9)(即10的9次方的意思)在進(jìn)行f[0]的初始化.因?yàn)榭赡軙?huì)出現(xiàn)負(fù)數(shù)
LIS模板題
有N個(gè)整數(shù),輸出這N個(gè)整數(shù)的最長(zhǎng)上升序列、最長(zhǎng)下降序列、最長(zhǎng)不上升序列和最長(zhǎng)不下降序列。
根據(jù)上述分析,我們應(yīng)該十分容易就能夠?qū)懗鲎詈蟮某绦?#xff0c;四個(gè)序列的代碼基本相似
代碼如下:
轉(zhuǎn)載于:https://www.cnblogs.com/pigzhouyb/p/10119863.html
總結(jié)
以上是生活随笔為你收集整理的lis最长上升子序列o(nlogn)优化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C# 去重处理字符大小写
- 下一篇: bugku-杂项 convert