LIS是要計算一個序列中最長的遞增子序列,例如
[10, 9, 2, 5, 3, 7, 101, 18]
這個序列中的最長遞增子序列可以是[2, 3, 7, 101]
,LIS的答案可以不為一,可能有很多種答案,因此我們要求的是他的最長長度。而我將會介紹和的做法!
我們可以直接窮舉先前每項數字的LIS,並去計算哪一種的長度最長。
定義 表示以為結尾的最長遞增子序列。
因此要計算 需要去檢查小於的所有位置,假設是我們要去檢查的位置,如果 並且 則可以把 為結尾的LIS接上來,最長遞增子序列的值就會變成 ,當然啦我們是要求最長的,所以即使有非常多種答案,我們最終要取最好的,因此我們可以得到轉移式
那在leetcode的300題(題目)就是LIS的裸題,可以去練習看看。
那接下來做做看這一題CSES 1145: Increasing Subsequence,當你很開心的寫下下面這個code
發現竟然吃了TLE,可以看到這一題的限制是,顯然不會過,因此我要來介紹的解法
接下來要介紹的作法,的做法我會兩種,一種是用二分搜去優化,另一種是使用資料結構做優化。
這個作法是利用二分搜去優化。
首先我們先用讀入測資,接著創建一個vector,稱作好了,存儲目前為止找到的最長遞增子序列,我們要利用紀錄第個位子的最小值,也就是說是要紀錄在的時候最小的,並更有潛力的建構出LIS。接著再利用二分搜去更新我們的。
我拿上面那個例子示範一次,arr = [10, 9, 2, 5, 3, 7, 101, 18]
。
v={10}
v={9}
v={2}
v={2, 5}
v={2, 3}
v={2, 3, 7}
v={2, 3, 7, 101}
v={2, 3, 7, 18}
在建構LIS的過程中可以發現裡面的數字具有單調性,因此在尋找的時候可以使用二分搜。
那用這個方法解CSES那一題就留給大家做做看,因為那一題我想用下一個做法示範一下,嘿嘿。
在這個方法中我使用BIT(Binary Indexed Tree)來做優化,BIT通常是處理區間問題,BIT可以用的時間做查詢(query),和的時間做更新(update),利用這個特性可以在下找出LIS。
首先定義是區間的LIS長度,接著列出轉移式:
會發現其實在求的是前綴最大值,發現到這間件事情之後便可以巧妙的利用BIT去優化。
這邊我們BIT是要對值域做更新和查詢,記得是「值域」喔,所以每一次做更新的時候我們要先找比該元素小1的值做查詢,查看比該元素小的LIS長度是多少,同樣使用這個例子[10, 9, 2, 5, 3, 7, 101, 18]
。
那一樣我先放上LeetCode的那一題的參考做法
再來就是我們的重頭戲了CSES 1145: Increasing Subsequence的的做法
我稍微講一下這一題,Constraints是,,由於是對值域做查詢和更新,按照題目的限制陣列會爆,因此要先做離散化,做完離散化之後就按照上面的步驟處理,最後找尋就是我們的答案了!
優化後的做法真的好神奇好美妙喔,希望大家都能一起欣賞這個優化的美,太喜歡了~~~
太開心了我寫完這篇LIS的介紹了!