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