1. 程式人生 > >最長遞增子序列 O(NlogN)演算法

最長遞增子序列 O(NlogN)演算法

今天回顧WOJ1398,發現了這個當時沒有理解透徹的演算法。
看了好久好久,現在終於想明白了。
試著把它寫下來,讓自己更明白。

最長遞增子序列,Longest Increasing Subsequence 下面我們簡記為 LIS。
排序+LCS演算法 以及 DP演算法就忽略了,這兩個太容易理解了。

假設存在一個序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出來它的LIS長度為5。
下面一步一步試著找出它。
我們定義一個序列B,然後令 i = 1 to 9 逐個考察這個序列。
此外,我們用一個變數Len來記錄現在最長算到多少了

首先,把d[1]有序地放到B裡,令B[1] = 2,就是說當只有1一個數字2的時候,長度為1的LIS的最小末尾是2。這時Len=1


然後,把d[2]有序地放到B裡,令B[1] = 1,就是說長度為1的LIS的最小末尾是1,d[1]=2已經沒用了,很容易理解吧。這時Len=1

接著,d[3] = 5,d[3]>B[1],所以令B[1+1]=B[2]=d[3]=5,就是說長度為2的LIS的最小末尾是5,很容易理解吧。這時候B[1..2] = 1, 5,Len=2

再來,d[4] = 3,它正好加在1,5之間,放在1的位置顯然不合適,因為1小於3,長度為1的LIS最小末尾應該是1,這樣很容易推知,長度為2的LIS最小末尾是3,於是可以把5淘汰掉,這時候B[1..2] = 1, 3,Len = 2

繼續,d[5] = 6,它在3後面,因為B[2] = 3, 而6在3後面,於是很容易可以推知B[3] = 6, 這時B[1..3] = 1, 3, 6,還是很容易理解吧? Len = 3 了噢。


第6個, d[6] = 4,你看它在3和6之間,於是我們就可以把6替換掉,得到B[3] = 4。B[1..3] = 1, 3, 4, Len繼續等於3

第7個, d[7] = 8,它很大,比4大,嗯。於是B[4] = 8。Len變成4了

第8個, d[8] = 9,得到B[5] = 9,嗯。Len繼續增大,到5了。

最後一個, d[9] = 7,它在B[3] = 4和B[4] = 8之間,所以我們知道,最新的B[4] =7,B[1..5] = 1, 3, 4, 7, 9,Len = 5。

於是我們知道了LIS的長度為5。

!!!!! 注意。這個1,3,4,7,9不是LIS,它只是儲存的對應長度LIS的最小末尾。有了這個末尾,我們就可以一個一個地插入資料。雖然最後一個d[9] = 7更新進去對於這組資料沒有什麼意義,但是如果後面再出現兩個數字 8 和 9,那麼就可以把8更新到d[5], 9更新到d[6],得出LIS的長度為6。


然後應該發現一件事情了:在B中插入資料是有序的,而且是進行替換而不需要挪動——也就是說,我們可以使用二分查詢,將每一個數字的插入時間優化到O(logN)~~~~~於是演算法的時間複雜度就降低到了O(NlogN)~!

程式碼如下:

//在非遞減序列 arr[s..e](閉區間)上二分查詢第一個大於等於key的位置,如果都小於key,就返回e+1
int upper_bound(int arr[], int s, int e, int key)
{
    int mid;
    if (arr[e] <= key)
        return e + 1;
    while (s < e)
    {
        mid = s + (e - s) / 2;
        if (arr[mid] <= key)
            s = mid + 1;
        else
            e = mid;
    }
    return s;
}

int LIS(int d[], int n)
{
    int i = 0, len = 1, *end = (int *)alloca(sizeof(int) * (n + 1));
    end[1] = d[0]; //初始化:長度為1的LIS末尾為d[0]
    for (i = 1; i < n; i++)
    {
        int pos = upper_bound(end, 1, len, d[i]); //找到插入位置
        end[pos] = d[i];
        if (len < pos) //按需要更新LIS長度
            len = pos;
    }
    return len;
}

相關推薦

遞增序列 O(NlogN)演算法 ( DP + 二分查詢)

看了好久好久,現在終於想明白了。 試著把它寫下來,讓自己更明白。 最長遞增子序列,Longest Increasing Subsequence 下面我們簡記為 LIS。 排序+LCS演算法 以及 DP演算法就忽略了,這兩個太容易理解了。 假設存在一個序列d[1..9] = 2 1 5 3 6 4 8 9 7,

遞增序列 O(NlogN)演算法

今天回顧WOJ1398,發現了這個當時沒有理解透徹的演算法。看了好久好久,現在終於想明白了。試著把它寫下來,讓自己更明白。最長遞增子序列,Longest Increasing Subsequence 下面我們簡記為 LIS。排序+LCS演算法 以及 DP演算法就忽略了,這兩

遞增序列O(NlogN)演算法(leetcode 300. Longest Increasing Subsequence )

最長遞增子序列,Longest Increasing Subsequence 下面我們簡記為 LIS。排序+LCS演算法 以及 DP演算法就忽略了,這兩個太容易理解了。假設存在一個序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出來它的LIS長度為5。下面

dp遞增序列nlogn演算法實現

n2的演算法很簡單,這裡是利用另外一個數組d,d[i]存的是長度為i的最末尾元素。因為d[i]是有序的,可以用二分查詢優化,複雜度降低為nlogn,程式碼如下,注意二分查詢的邊界: #include

上升序列O(nlogn) 要強的T^T(2358)

中間 數學 個數 img NPU 序列 ger main col 題目來源:http://120.78.128.11/Problem.jsp?pid=2358 要強的T^T TimeLimit:1000MS MemoryLimit:65536K 64-bit in

洛谷P1439 公共序列(O(nlogn)公共序列模板)

題目描述: 給出1-n的兩個排列P1和P2,求它們的最長公共子序列。 題解: 此題要求O(nlogn)解決LCS。 我們考慮LCS轉LIS,二分維護。 我們保證一個序列是單調的,那麼將另一個序列按照標號排序,會發現轉換後的LIS就是原序列的LCS。 但是這種解法有一個限制——兩個排列要求是1~n的

Luogu 1439 公共序列 O(NlogN)做法

對於一般的最長公共子序列問題(LCS),我們有O(N ^ 2)的DP做法 易得狀態轉移方程為 N=100000的資料範圍而言,這種做法無論在時間上還是空間上都是無法通過的 但LCS問題有一個特殊點,即S1與S2都是一段相同數字集合的不同排列 而對於100

遞增序列問題(演算法導論作業15.4-5、15.4-6)

問題描述 對於長度為n的序列S[1...n],找出長度最大的子序列,其子序列的每個元素均遞增。 15.4-5、時間複雜度O(n^2) 剛看到這題時,想到了個投機取巧的方法。因為書中此節介紹了LCS(最長公共子序列)演算法,於是可以直接將這個序列排序O(nlogn),然後

上升序列 O(nlogn)解法 (轉)

最近在做單調佇列,發現了最長上升子序列O(nlogn)的求法也有利用單調佇列的思想。    最長遞增子序列問題:在一列數中尋找一些數,這些數滿足:任意兩個數a[i]和a[j],若i<j,必有a[i]<a[j],這樣最長的子序列稱為最長遞增子序列。   設dp[i]表示以i為結尾的最長遞增子序列的長

遞增序列 O nlgn時間複雜度

對於一個數字序列,請設計一個複雜度為O(nlogn)的演算法,返回該序列的最長上升子序列的長度,這裡的子序列定義為這樣一個序列U1,U2...,其中Ui < Ui+1,且A[Ui] < A[Ui+1]。 給定一個數字序列A及序列的長度n,請返回最長上升子序列的長度。 測試樣例: [2,1,4

每日三題-Day3-C(HDU 1257 最少攔截系統 上升序列O(nlogn) )

原題地址 最少攔截系統 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 40250 Accepted Submi

1836 Alignment (上升序列 O(nlogn))

解題思路:是POJ2533的擴充套件題。 題意不難,令到原佇列的最少士兵出列後,使得新佇列任意一個士兵都能看到左邊或者右邊的無窮遠處。就是使新佇列呈三角形分佈就對了。 但這裡有一個陷阱,看了一些別人的解題報告說“任一士兵旁邊不能存在等高的士兵”,然後又舉了一個例

每日三題-Day5-A(POJ 2533 Longest Ordered Subsequence 上升序列O(nlogn)解法)

Longest Ordered Subsequence Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 51451 Accepted: 22885 Description A numeric

遞增序列O(n^2)版

#include<iostream> #include<string.h> using namespace std; #define max(a, b)(a>b ? a:b) int solve(int *num, int n) { in

遞增序列LIS的O(nlogn)的求法

最長遞增子序列(Longest Increasing Subsequence)是指n個數的序列的最長單調遞增子序列。比如,A = [1,3,6,7,9,4,10,5,6]的LIS是1 3 6 7 9 10。我們現在希望程式設計求出一個給定的陣列,我們能得到LIS

遞增序列,時間複雜度(Onlogn))

package com.kailong.datastures; import java.util.Arrays; /** * Created by Administrator on 2017/4/17. * 最長遞增子序列 */ public class Find

LeetCode 遞增序列O(nlogn)詳解

/***************************************************************************** * * Given an unsorted array of integers, find t

遞增序列 LIS 時間複雜度O(nlogn)的Java實現

關於最長遞增子序列時間複雜度O(n^2)的實現方法在部落格http://blog.csdn.net/iniegang/article/details/47379873(最長遞增子序列 Java實現)中已經做了實現,但是這種方法時間複雜度太高,查閱相關資料後我發現

單調遞增序列O(nlogn)

#include "iostream" #include "fstream" using namespace std; /* b[k]表示長度為i的子序列c[i]中,長度為k的最長單調遞增子序列的最小

遞增序列 時間複雜度:O(NlogN

假設存在一個序列d[1..9] = 2 1 5 3 6 4 8 9 7,可以看出來它的LIS長度為5。 下面一步一步試著找出它。 我們定義一個序列B,然後令 i = 1 to 9 逐個考察這個序列。 此外,我們用一個變數Len來記錄現在最長算到多少了 首先,把d[1]有序地放