1. 程式人生 > >【字串系列】最長上升子序列(LIS)

【字串系列】最長上升子序列(LIS)

LIS(Longest increasing subsequence) 主要有O(nlogn)和O(n^2)兩種解法,本博文主要介紹O(nlogn)解法,順便提一下O(n^2)。

首先說一下O(n^2)解法的思路,假設一個數組a[n],定義dp[i]為以a[i]結尾的LIS的值,那麼對於a[i],有dp[i]=max{dp[k],k∈[1,i-1]且a[k]<a[i]}+1,迭代求解,dp[n]就是最終結果

下面解釋一下O(nlogn)的解法:

假設存在一個序列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)~!

解釋摘抄自:http://blog.csdn.net/shuangde800/article/details/7474903

如果看懂上面的解釋的話,那麼總結地來說,做法就是:假設原陣列為a,定義一個d陣列,對於a[i],在陣列d二分查詢,找到一個k位置,使得d[k-1]<a[i],

d[k]>a[i],用a[i]替換d[k],如果a[i]大於d陣列的所有數,則插入到d陣列,如果a[i]小於d陣列的所有數,則替換d[0],就這樣迭代下去,最終結果就是d陣列的長

度。

再簡單的來說就是通過二分查詢,找不到的時候返回比它大的第一個數的位置,然後替換。這個二分查詢的模板可以參考我的另一篇文

章:http://blog.csdn.net/qq_22497299/article/details/52594820

hdu1025(模板題)

#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cstdlib>
#define INF 0x3f3f3f3f
using namespace std;
int arr[500005],res[500005],n,ans,time=0;


int search(int a, int b, int k){
	int mid;
	while(a<=b){
		mid = (a+b)/2;
		if(k>res[mid]) a = mid+1;
		else b = mid-1;
	}
	return a;
}

void LIS(){
	int t;
	res[1] = arr[1];
	ans = 1;
	for(int i=2;i<=n;i++){
		t = search(1, ans, arr[i]);
		if(res[t] >= arr[i]) res[t] = arr[i];
		else res[++ans] = arr[i];
	}
    if(ans==1)
		printf("Case %d:\nMy king, at most 1 road can be built.\n\n",++time);
    else
		printf("Case %d:\nMy king, at most %d roads can be built.\n\n",++time,ans);
}

int main()
{
	//freopen("input.txt","r",stdin);
	int a,b;
	while(scanf("%d",&n)!=EOF){
		memset(res,0,sizeof(res));
		for(int i=0;i<n;i++){
			scanf("%d%d",&a,&b);
			arr[a] = b;
		}
		LIS();
	}
	return 0;
}


hdu1950(還是模板題)

#include<iostream>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cstdlib>
#define INF 0x3f3f3f3f
using namespace std;
int dp[40005],arr[40005];
int n,p,t;

int search(int a, int b, int k){
	int mid;
	while(a<=b){
		mid = (a+b)/2;
		if(k>dp[mid]) a = mid + 1;
		else b = mid - 1;
	}
	return a;
}

void LIS(){
	memset(dp,0,sizeof(dp));
	int ans = 1;
	dp[1] = arr[1];
	for(int i=2;i<=p;i++){
		int t = search(1,ans,arr[i]);
		if(dp[t]>=arr[i]) dp[t] = arr[i];
		else dp[++ans] = arr[i];
	}
	cout<<ans<<endl;
}

int main()
{
	//freopen("input.txt","r",stdin);
	cin>>n;
	while(n--){
		cin>>p;
		for(int i=1;i<=p;i++){
			scanf("%d",&t);
			arr[i] = t;
		}
		LIS();
	}
	return 0;
}


 hdu4521

 變形LIS問題,題意是求相隔d個數的最長上升子序列,多用一個數組,記錄在i點時最長的遞增子序列長度,採用延遲更新的方法,在i位置時更新i-d的位置

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define INF 0x3f3f3f3f
using namespace std;
int n,d,arr[100005],dp[100005],mark[100005];

int search(int a, int b, int k){
	int mid;
	while(a<=b){
		mid = (a+b)/2;
		if(k>dp[mid]) a = mid+1;
		else b = mid-1;
	}
	return a;
}

void LIS(){
	memset(dp,INF,sizeof(dp));
	int ans = 0;
	for(int i=1;i<=n;i++){
		mark[i] = search(1,ans,arr[i]);
		if(mark[i]>ans)
			ans = mark[i];
		if(i-d>0 && dp[mark[i-d]]>arr[i-d])
			dp[mark[i-d]] = arr[i-d];
	}
	cout<<ans<<endl;
}

int main()
{
//	freopen("input.txt","r",stdin);
	while(scanf("%d%d",&n,&d)!=EOF){
		for(int i=1;i<=n;i++){
			scanf("%d",&arr[i]);
		}
		LIS();
	}
	return 0;
}


相關推薦

字串系列上升序列LIS

LIS(Longest increasing subsequence) 主要有O(nlogn)和O(n^2)兩種解法,本博文主要介紹O(nlogn)解法,順便提一下O(n^2)。 首先說一下O(n^2)解法的思路,假設一個數組a[n],定義dp[i]為以a[i]結尾的LI

初入DP:上升序列LIS

Description 我們都知道上體育課時,體育老師會讓我們按身高從小到大(或從大到小)排成一排。可是近日體育老師周老師卻有點煩心, 他教的班級來了幾個插班生,可他們有的不守規矩,沒有按照身高大小來插入隊伍,導致隊伍很難看。現在周老師有一個問 題:給定一排人的身高,問能否至多去掉一個人,使

動態規劃 上升序列LIS

O(N2)寫法: memset(dp, 0, sizeof(dp)) for(i = 0; i < n; i++) {          dp[i]= 1;          for(j= 0; j < i; j++) {                   

上升序列LIS長度 Onlogn演算法 hdu1950為例

最長上升子序列 最長上升子序列(Longest Increasing Subsequence,LIS),是指一個序列中最長的單調遞增的子序列。 該問題有一個n2的動態規劃解法,這裡介紹O(nlogn)的解法。 設a[]是原序列,d[i]表示長度為i的上升子

bzoj5427上升序列貪心+LIS

貪心 去掉 ati lse com set event color tps   題目傳送門:https://www.lydsy.com/JudgeOnline/problem.php?id=5427   因為noip,博客咕了好久,這幾天集中填一下坑。   這題我們可以

例題動規上升序列

最長上升子序列 有N個整數構成的序列,請找出其中長度最長的上升子序列的長度. 思路: 1、階段:從右到左依次討論每個數字。 2、狀態:f[i]表示從第i個數開始向右能夠得到的最長上升序列的長度

bzoj 3173上升序列

傳送門~ 解題思路 因為是1~n順序插入,所以新插入元素不會對之前已經求出的值產生影響。以新插入元素為結尾的最長上升子序列為它插入位置之前所有元素的maxx加1。用平衡樹維護,支援插入和查詢。 程式碼: #include<cstdio>

上升序列dp

tps ann test case std intro lar ont sta into 鏈接:https://www.nowcoder.com/questionTerminal/d83721575bd4418eae76c916483493de來源:牛客網廣場上站著一支隊伍

dp-遞增序列 LIS

一個數 bsp 註意 str 只有一個 自然 end alt ace 首先引出一個例子 問題 :   給你一個長度為 6 的數組 , 數組元素為 { 1 ,4,5,6,2,3,8 } , 則其最長單調遞增子序列為 { 1 , 4 , 5 , 6 , 8 } , 並且長度

動態規劃之遞增序列LIS

lib sca while -c -o 組成 describe log ret 在一個已知的序列{ a1,a2,……am}中,取出若幹數組成新的序列{ ai1, ai2,…… aim},其中下標 i1,i2, ……im保持遞增,即新數列中的各個數之間依舊保持原

遞增序列LIS

本篇部落格主要講述什麼是最長公共子序列、求解最長公共子序列的思想,以及程式碼。 什麼是最長公共子序列?   給定一個長度為N的陣列,找出一個最長的單調自增子序列(不要求是連續的)。例如:6 5 7 8 4 3 9 1,這裡的最長遞增子序列是{ 6, 7, 8, 9}或者{

BZOJ3173: [Tjoi2013]上升序列樹狀數組

nss 貢獻 isp 轉化 復雜 src printf efi col 【題意】給定ai,將1~n從小到大插入到第ai個數字之後,求每次插入後的LIS長度。 【算法】樹狀數組||平衡樹 【題解】 這是樹狀數組的一個用法:O(n log n)尋找前綴和為k的最小位置。(當數列

bzoj5161上升序列 狀壓dp+打表

-s 只需要 [] sca div limits pow 證明 AC 題目描述 現在有一個長度為n的隨機排列,求它的最長上升子序列長度的期望。 為了避免精度誤差,你只需要輸出答案模998244353的余數。 輸入 輸入只包含一個正整數n。N<=28 輸出

bzoj3173上升序列

節點 return hup 位置 一個 ostream online %d pre Portal --> bzoj3173 Solution   感覺自己需要智力康復qwq   首先題目給的這個序列肯定是一個\(1-n\)的排列,並且插入的順序是從小到大   仔細思考

bzoj5427上升序列

題目連結 額。。我簡直像個智障啊。。想了一個錯的解還給學弟們比比半天。。然後學弟們也沒指出錯誤。。 首先考慮最優方案肯定是把所有的 N N N都裝進去 為什麼呢? (因為沒有找到證明,智障我就自

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

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

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

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

動態規劃公共序列問題

clas == 搜索 ios for 參考 pan 公式 是否 題目描述: 給定兩個字符串s1s2……sn和t1t2……tn。求出這兩個字符串最長的公共子序列的長度。字符串s1s2……sn的子序列指可以表示為si1si2……sim(i1<i2<……<im)

TOJ 5065連續序列

scanf sca body 一行 desc pre AC bsp end Description 給定一系列非負整數,求最長的連續子序列,使其和是7的倍數。 Input 第一行為正整數N(1<=N<=50000),接下來有N行,每行有一個非負整數,所有整數不大

動態規劃公共序列公共

1. 問題描述 子串應該比較好理解,至於什麼是子序列,這裡給出一個例子:有兩個母串 cnblogs belong 比如序列bo, bg, lg在母串cnblogs與belong中都出現過並且出現順序與母串保持一致,我們將其稱為公共子序列。最長公共子序列(Longest Common Subsequence