1. 程式人生 > 其它 >最長上升子序列 II(LIS)————二分優化

最長上升子序列 II(LIS)————二分優化

技術標籤:動態規劃演算法動態規劃

題目連結
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; }