最長上升子序列 II(LIS)————二分優化
阿新 • • 發佈:2021-01-26
題目連結
https://www.acwing.com/problem/content/898/
思路
這題說是dp,但是分析完之後更像貪心了,首先舉個例子比如一個序列:3 1 2 1 8 5 6。(後面下標從1開始計)那麼對於 a [ 1 ]和a [ 2 ]來說,如果上升子序列長度為2,那麼能接在 a [ 1 ]後面的也一定能接在 a [ 2 ]後面,因為a [ 2 ] < a[ 1 ]。以此思想,引出我們 dp陣列 f 的狀態表示。
我們的狀態表示為 f [ i ] 存放的是所有長度為 i 的上升子序列中所有末尾值中的最小值,這樣就能得到一個嚴格的單調遞增序列,我們來證明一下為什麼嚴格單調遞增,假設 f [ 6 ] ≤ f [ 5 ],那麼對於長度為6的這個上升子序列來說,它的第五個數 t 一定是小於 f [ 6 ]的,那麼就存在 f [ 5 ] > t ,但是我們存放的是所有長度為5的上升子序列的末尾值中的最小值,所有矛盾了,故這個序列一定是單調遞增的。那麼對於 a [ i ]我們只需要查詢 f 陣列中小於 a [ i ] 的最大值 j 即可,然後可以直接更新f [ j+1] = a [ i ],因為 a [ i ]一定小於當前 f [ j + 1 ],不然也不會找到 j 了,那麼這個查詢到 j 的過程就是可以運用二分查詢來優化,所以整個演算法的時間複雜度就由樸素做法的O(n²)優化到O(nlogn)。
程式碼
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int a[N];
int f[N];
int n;
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
f[0]=-2e9;//哨兵作用
int len=0;
for(int i=0;i<n;i++)
{
int l=0,r=len;
while(l<r)
{
int mid=(l+r+1)>>1;//向上取整
if(f[mid]<a[i]) l=mid;
else r=mid-1;
}
len=max(len,r+1);
f[r+1]=a[i];
}
printf("%d\n",len);
return 0;
}