1. 程式人生 > >最長上升子序列(LIS) 三種方法:O(nlogn,DP,LCS)

最長上升子序列(LIS) 三種方法:O(nlogn,DP,LCS)

參考:

紫書P274~275

題目大意:

    給定n個整數A1,A2...An,從左到右的順序選出儘量多的整數,組成一個上升子序列(子序列可以理解為:刪除0個或多個數,其他數的順序不變。)例如序列1,6,2,3,7,5,可以選出上升子序列·1,2,3,5,也可以選出1,6,7,但前者跟車,選出的上升子序列中相鄰的元素不能相等。(紫書P274)

解題思路:

O(nlogn):(程式碼一)

   我是從紫書上看到這道題的,所以先寫按dp寫的,不過看到說有O(nlogn)的演算法,我就搜了一下,找到了一篇大神寫得(上邊有地址),寫得很詳細,用了一個例子把計算過程演示了一遍,很清晰。  整個過程需要兩個陣列:d[n]用來儲存序列,B[n]用來儲存:序列中長度為j的最長上升子序列的最小末尾,並且用len表示當前B內已經儲存的元素個數。然後迭代d陣列中的每一個元素:找到b[i]在中B[len]的最下屆pos,如果當前d[i] > B[len],pos == len+1,則將d[i]存入B[++len]中。如果d[i] < B[len],則pos<len,應將B[pos]更新為b[i],len不變。迭代完之後,就可以求出LIS的長度。

動態規劃(O(n²)):(程式碼二)

 由於我是看著紫書寫的動態規劃的程式碼,所以思想和紫書上是一樣的:狀態:用d(i)表示以i結尾的最長上升子序列的長度,則d(i)=max{0,d(j)|j<i,Aj<Ai}+1,最終答案是max{d[i]}.

使用LCS間接計算(O(n²)):(程式碼三)

  思路很簡單就是使用另一個數組來儲存序列,然後進行排序,然後求原序列和排序後序列的LCS(最長公共子序列),就可以得到LIS。狀態:使用d(i,j)來儲存A1,A2...Ai和B1,B2,...Bj的LIS長度,狀態轉移方程:如果Ai==Bj,則d(i,j)=d(i-1,j-1)+1。否則d(i,j) = max{d(i-1,j),d(i,j-1)}。最終d[n][n]就是LIS和LCS的長度。

遇到問題:

 在寫第三中方法的時候是用了memcpy函式,memcpy(a,b,sizeof(a))可以得出結果,memcpy(a,b,n+1)就不對,只能複製一部分,很迷。。

程式碼:

程式碼一:

#include<iostream>
#include<algorithm>

using namespace std;

const int MAXN = 100000 + 10;

int n, list[MAXN], d[MAXN];//d(i):長度為i的最長上升子序列的最小末尾

int main()
{
	while (cin >> n) {
		for (int i = 1; i <= n; i++) cin >> list[i];
		list[0] = -1; d[0] = -1;
		int len = 0;
		for (int i = 1; i <= n; i++) {
			if (list[i] != d[len]) {
				int pos = lower_bound(d + 1, d + len + 1, list[i]) - d;
				d[pos] = list[i];
				if (list[i] > d[len]) len++;
			}
		}
		cout << len << endl;
	}
	return 0;
}

程式碼二(TLE):

#include<iostream>
#include<algorithm>

using namespace std;

const int MAXN = 100000 + 10;

int n, list[MAXN], d[MAXN];//d(i):以i結尾的最長上升子序列的長度
//轉移方程d(i) = max{0,d(j)|i >j,Ai > Aj}+1

int dp()
{
	int max_len = 0;
	for (int i = 1; i <= n; i++) {
		for (int j = 0; j < i; j++) {
			if (list[j + 1] > list[j])
				d[i] = max(0, d[j]) + 1;
		}
		max_len = max(max_len, d[i]);
	}
	return max_len;
}

int main()
{
	while (cin >> n) {
		for (int i = 1; i <= n; i++) cin >> list[i];
		list[0] = -1; d[0] = 0;
		cout << dp() << endl;
	}
	return 0;
}


程式碼三(MLE):

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int MAXN = 10000 + 10;

int n, list[MAXN],srt_list[MAXN], d[MAXN][MAXN];

int main()
{
	while (cin >> n) {
		for (int i = 1; i <= n; i++) { cin >> list[i]; srt_list[i] = list[i]; }
		sort(srt_list, srt_list + n + 1);
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= n; j++) {
				if (list[i] == srt_list[j]) {
					d[i][j] = d[i - 1][j - 1] + 1;
				}
				else {
					d[i][j] = max(d[i - 1][j], d[i][j - 1]);
				}
			}
		}
		cout << d[n][n] << endl;
	}
	return 0;
}



相關推薦

上升序列(LIS) 方法O(nlogn,DP,LCS)

參考: 紫書P274~275 題目大意:     給定n個整數A1,A2...An,從左到右的順序選出儘量多的整數,組成一個上升子序列(子序列可以理解為:刪除0個或多個數,其他數的順序不變。)

上升序列(兩方法

常規方法:(n^2) #include<iostream> using namespace std; int i,j,n,a[100],b[100],max; int main() { cin>>n; for(i=0;i<n;i++)

上升序列的兩演算法(LIS

最長上升子序列就是求:給定的一串數字中 找出不斷上升的最長那一串(該串不一定相連,只保證遞增即可)。就比如下面這個例子   其最長上升子序列為2 3 4 7或2 3 4 6  數串長度為4 那麼具體怎麼求的呢 我們拿一個最簡單的例子講: 【題目描述】 給定N個數,求這N

上升序列LIS(雲筆記圖片版)

ima 筆記 img 最長上升子序列 lis 技術 logs ges mage 最長上升子序列LIS(雲筆記圖片版)

上升序列LIS

namespace div 什麽 space 2個 當前 這不 names end 題目:給你一個長度為n的序列,讓你找到最長上升子序列的長度. 分析: 這個是dp的經典問題,今天拿來重新學習一下。 如果按照貪心的想法,是很容易看到有問題的,比如一串數字 1,3,-3,-2

上升序列(LIS)的n*log(n)求法

一個 末尾 貪心 當前 大量 排序 正常 方法 二分 方法: 對於某個序列,設一個數組,將序列第一個數放入,然後再一個一個判斷序列下一位,如果大於當前數組的末尾元素,則加入數組,否則利用二分法找到第一個大於等於當前數的元素並替換,最後這個數組的長度len就是最長上升子序列的

動態規劃-上升序列(LIS)

時間複雜度為〇(nlogn)的演算法,下面就來看看。 我們再舉一個例子:有以下序列A[]=3 1 2 6 4 5 10 7,求LIS長度。 我們定義一個B[i]來儲存可能的排序序列,len為LIS長度。我們依次把A[i]有序地放進B[i]裡。(為了方便,i的範圍就從1~n表示第i個數) A[1]=3,把

上升序列 LIS演算法實現

最長上升子序列LIS演算法實現 LIS(Longest Increasing Subsequence)最長上升(不下降)子序列 有兩種演算法複雜度為O(n*logn)和O(n^2)。在上述演算法中,若使用樸素的順序查詢在D1..Dlen查詢,由於共有O(n)個元素需要計算,每次計算時的複雜度

上升序列——LISO(nlogn)演算法(二分)

LIS的O(nlogn)演算法(二分) 傳送門:點我 O(n^2)解法:(n為4w,TLE) memset(dp,1,sizeof(dp)); int ans=-1; for(i=2; i<=n; i++) {     for(j=1; j<i; j++)

動態規劃法求上升序列(LIS)

最長上升子序列(LIS)是指一個序列中最長的單調遞增的子序列,對於任意的i<j都滿足ai<aj的子序列。 下面我們來介紹兩種dp來求LIS。 方法1: 我們首先來建立一下遞推關係: 定義dp[i]:為以ai為末尾的最長上升子序列的長度。 以 ai 結尾的上升子序列是: (1)只包含 a

公共序列LCS問題轉化為上升序列LIS問題

先看題目 題目描述 給出1-n的兩個排列P1和P2,求它們的最長公共子序列。 輸入輸出格式 輸入格式: 第一行是一個數n, 接下來兩行,每行為n個數,為自然數1-n的一個排列。 輸出格式: 一個數,即最長公共子序列的長度 輸入輸出樣例 輸入樣例#1: 5 3 2

遞增序列演算法

問題 給定一個長度為N的陣列,找出一個最長的單調自增子序列(不一定連續,但是順序不能亂)。例如:給定一個長度為6的陣列A{5, 6, 7, 1, 2, 8},則其最長的單調遞增子序列為{5,6,7,8},長度為4. 解法1:最長公共子序列法 這個問題可以轉換為最長公共子序列問題。如例子中的陣列A{5,

【經典動態規劃問題】上升序列 LIS

目錄   最長上升子序列: O(N^2)動態規劃: O(N*logN):貪心+二分 最長上升子序列: 一個數的序列bi,當b1 < b2 < … < bS的時候,我們稱這個序列是上升的。對於給定的一個序列(a1, a2, …, aN),我們可以

上升序列的兩演算法

最長上升子序列英文全稱:Longest Increasing Subsequence 一.O(n*n)演算法,dp[i]表示以ai為末尾的最長上升子序列的長度,而以ai結尾的最長上升子序列有兩種

上升序列(LIS)-O(nlogn)演算法總結.

最近做了poj-1836這道題,用O(n^2)的LIS演算法TLE了,百度了很久大概的學會了LIS的O(nlogn)演算法; 最長上升子序列,簡稱LIS,O(n^2)的演算法這裡我就不寫了,百度一

洛谷【P1020】關於上升序列的四解法

這是第一次寫部落格,參加學校的acm已經有一年的時間了,現在才開始寫,說實話有點晚,但再不寫就更晚了,現在在做最長子序列方面的題,找了一道比較經典的題來舉例。 題目連結:https://www.luogu.org/problemnew/show/P1020

上升序列(LIS)長度的O(nlogn)演算法

hdu 1950 Bridging signals =================================== 最長上升子序列(LIS)的典型變形,熟悉的n^2的動歸會超時。LIS問題可以優化為nlogn的演算法。 定義d[k]:長度為k的上升子序列的最末元素

jzoj 3467. 【NOIP2013模擬聯考7】上升序列(lis) dfs+lis+手工棧

Input 輸入檔案lis.in的第一行有一個正整數n,表示操作個數。接下來n行每行有兩個整數op,x。如果op為0,則表示新增x這個數字;如果op為1,則表示回到第x次操作之後。 Output 對於每次操作,在輸出檔案lis.out中輸出一個

NYOJ 79 攔截導彈 (經典dp上升序列 LIS

描述 某國為了防禦敵國的導彈襲擊,發展中一種導彈攔截系統。但是這種導彈攔截系統有一個缺陷:雖然它的第一發炮彈能夠到達任意的高度,但是以後每一發炮彈都不能高於等於前一發的高度。某天,雷達捕捉到敵

【部分轉載】【lower_bound、upperbound講解、二分查詢、上升序列(LIS)、下降序列模版】

二分 lower_bound lower_bound()在一個區間內進行二分查詢,返回第一個大於等於目標值的位置(地址) upper_bound upper_bound()與lower_bound()的主要區別在於前者返回第一個大於目標值的位置 int lowerBound(int x){ i