最長上升子序列(LIS)長度 O(nlogn)演算法 hdu1950為例
阿新 • • 發佈:2019-01-09
最長上升子序列
最長上升子序列(Longest Increasing Subsequence,LIS),是指一個序列中最長的單調遞增的子序列。
該問題有一個
設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[]是單調遞增的,所以我們二分找,這樣最後總的時間複雜度就是 (nlogn)
這個寫法有個缺陷,就是不太好得出序列,只能得出序列長度。
比如序列1 20 8 9 7
最長上升子序列應該是1 8 9
但是d[]儲存的是1 7 9
動態規劃的
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;
}