1. 程式人生 > >最長上升子序列(LIS)長度 O(nlogn)演算法 hdu1950為例

最長上升子序列(LIS)長度 O(nlogn)演算法 hdu1950為例

最長上升子序列

最長上升子序列(Longest Increasing Subsequence,LIS),是指一個序列中最長的單調遞增的子序列。
該問題有一個n2的動態規劃解法,這裡介紹O(nlogn)的解法。

設a[]是原序列,d[i]表示長度為i的上升子序列的最末元素,若有多個長度為i的上升子序列,則記錄最小的那個最末元素。那麼d[]肯定是單調遞增的(後面會用到這個性質)。
開始我們先令len=1,d[1]=a[1],之後遍歷a[],如果d[len] < a[i]那麼令d[++len]=a[i],否則就在d[]中找到d[k-1]<a[i]<d[k],然後更新d[k]=a[i]即可,這樣到最後len就是最長上升子序列的長度。因為d[]是單調遞增的,所以我們二分找,這樣最後總的時間複雜度就是O

(nlogn)

這個寫法有個缺陷,就是不太好得出序列,只能得出序列長度。
比如序列1 20 8 9 7
最長上升子序列應該是1 8 9
但是d[]儲存的是1 7 9
動態規劃的n2解法比較好得出序列,但是感覺時間複雜度又有點高,如果你知道怎麼在O(nlogn)的時間內(或更短)得出序列(注意不是序列長度),請留言指點我,謝謝。

hdu1950

題意
最開始一個數t表示t組樣例,之後每組樣例給一個數n(<=40000),之後給出n個數,求最長上升子序列的長度。

題解
裸題,直接寫。一般有兩種寫法,可以自己寫二分,可以用STL的lower_bound,下面用第二種寫法。

#include <bits/stdc++.h>
using namespace std; typedef long long ll; const int maxn = 4e4+5; int a[maxn],n; int d[maxn],len; int main() { int t; scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",a+i); len=1,d[1]=a[1],d[0]=0; for(int i=2;i<=n;i++) { if
(d[len]<a[i]) d[++len]=a[i]; else { int tmp = lower_bound(d,d+len,a[i])-d; d[tmp] = a[i]; } } printf("%d\n",len); } return 0; }