方法一:O(n2)的dphtml
狀態定義:dp[i] 表示以數 a[i] 結尾的 LIS 值ios
狀態轉移方程:dp[i] = max(dp[j] + 1, dp[i]) 1 <= j < i && a[i] > a[j]數組
代碼:優化
1 #include<iostream> 2 using namespace std; 3 const int maxn = 110; 4 int a[maxn], dp[maxn]; 5 int n, ans = -1; 6 int main() 7 { 8 cin >> n; 9 for(int i = 1; i <= n; i++) 10 { 11 cin >> a[i]; 12 dp[i] = 1; 13 } 14 for(int i = 1; i <= n; i++) 15 for(int j = 1; j < i; j++) 16 if(a[j] < a[i]) 17 dp[i] = max(dp[j] + 1, dp[i]); 18 for(int i = 1; i <= n; i++) 19 ans = max(ans, dp[i]); 20 cout << ans << endl; 21 return 0; 22 }
方法二:O(nlogn)的貪心+二分spa
引入一個數組,暫且命名爲 low[] ,它的含義是:low[i] 表示長度爲 i 的 LIS 序列的最小的最後一個元素。因此,low 數組裏的元素都是遞增的,對於一個遞增的序列,要使它「成長的潛力」儘量大,那麼它目前的最後一個元素就要儘量小。這個思路是正確的,那麼就是 low 數組的維護問題了,當目前遍歷到的這個數 a[i] 比 low 數組尾部數字大的時候,就把目前這個數加入到 low 數組尾部,不然,在low數組中找到第一個大於等於 a[i] 的元素 low[j] ,用 a[i] 去更新 low[j]。這裏就存在着時間的優化,若是你找第一個大於等於 a[i] 的元素時,從頭至尾遍歷,那麼最終的時間複雜度仍是O(n2),這裏能夠用二分來找,二分的複雜度是O(logn),因此總的時間複雜度就是O(nlogn)。.net
看看代碼吧:code
1 #include <iostream> 2 using namespace std; 3 const int maxn = 100010; 4 const int INF = 0x3f3f3f3f; 5 int low[maxn], a[maxn]; 6 int n, len; 7 int binary_search(int *a, int r, int x) 8 { 9 int l = 1, mid; 10 while(l <= r) 11 { 12 mid = (l + r) >> 1; 13 if(a[mid] <= x) 14 l = mid + 1; 15 else 16 r = mid - 1; 17 } 18 return l; 19 } 20 int main() 21 { 22 cin >> n; 23 for(int i = 1; i <= n; i++) 24 { 25 cin >> a[i]; 26 low[i] = INF; 27 } 28 low[1] = a[1]; 29 len = 1; 30 for(int i = 2; i <= n; i++) 31 { 32 if(a[i] >= low[len]) 33 low[++len] = a[i]; 34 else 35 low[binary_search(low, len, a[i])] = a[i]; 36 } 37 cout << len << endl; 38 return 0; 39 }
方法三:O(nlogn)的樹狀數組優化dphtm
關於這個方法,其實總體思路和第一種沒區別,只是在找數 a[i] 前面的LIS最大值的時候使用樹狀數組結構來優化效率。好比原序列 a[] ,複製一個同樣的序列 b[] ,對 b[] 進行排序和去重(不去重的話就不是嚴格的上升序列),而後遍歷 a[] 數組(由於子序列是創建在原序列的基礎上的),假設咱們遍歷到了 a[i] ,咱們要獲得原序列中在 a[i] 前面的數其中LIS值最大的那個且那個數要比當前 a[i] 小,這二者缺一不可,那麼好,b[] 數組恰好是排序後的數,找到數 a[i] 在 b[] 中的位置下標 p ,那麼也就是要在 b[] 數組中下標從1到p-1(假設咱們的數下標都是從1開始的)中找到LIS值最大的,這裏這個操做就能夠用樹狀數組的區間查詢來實現,設爲 query(p-1) ,則 ans = max(ans,query(p-1)+1) 就能獲得最終的LIS。blog
Reference:排序