1. 程式人生 > >LCS的優化演算法:O(nlogn)級別

LCS的優化演算法:O(nlogn)級別

一、參考連結:

1、演算法部分:http://www.cnblogs.com/itlqs/p/5743114.html

2、二分搜尋upper_bound and lower_bound:https://blog.csdn.net/qq_40160605/article/details/80150252

二、例題:

洛谷:P1020導彈攔截:https://www.luogu.org/problemnew/solution/P1020

題目大意:求最長不上升子序列與不上升子序列個數

思路分析:

1、最長不上升子序列的樸素dp為O(n2)級別,對於10W這樣的大資料還是處理不了,於是需要更為優秀的O(nlogn)級別的演算法。

具體思路如下:使用陣列d來為維護子序列,d[i]表示:長度為i的子序列最後一位的最大值

考慮新進來一個元素a[i]:

  如果這個元素大於等於d[len],直接讓d[len+1]=a[i],然後len++。這個很好理解,當前最長的長度變成了len+1,而且d陣列也添加了一個元素。

  如果這個元素小於d[len]呢?說明它不能接在最後一個後面了。那我們就看一下它該接在誰後面。

  準確的說,並不是接在誰後面。而是替換掉誰。因為它接在前面的誰後面都是沒有意義的,再接也超不過最長的len,所以是替換掉別人。那麼替換掉誰呢?就是替換掉那個最該被它替換的那個。也就是在d陣列中第一個

大於它的。第一個意味著前面的都小於等於它。假設第一個大於它的是d[j],說明d[1..j-1]都小於等於它,那麼它完全可以接上d[j-1]然後生成一個長度為j的不下降子序列,而且這個子序列比當前的d[j]這個子序列更有潛力(因為這個數比d[j]小)。所以就替換掉它就行了,也就是d[j]=a[i]。其實這個位置也是它唯一能夠替換的位置(前面的替了不滿足d[k]最小值的定義,後面替換了不滿足不下降序列)

  至於第一個大於它的怎麼找……STL upper_bound。每次複雜度logn。

2、不上升子序列個數,根據Dilworth定理得知:

(1)U的鏈劃分使用的最少集合數,等於它的最大反鏈長度。

(2)U的反鏈劃分使用的最少集合數,等於它的最大鏈長度。

(大概可以理解為,一個集合,其中的序列最小劃分數等於反向的最長序列長度)

根據這個定理,就可以得知,不上升子序列的數量就是上升子序列的最大長度(而不下降的相反就是下降

所以答案就是上升子序列的最大長度,解決方法如(1)

AC程式碼:

#include<bits/stdc++.h>
#define MAX_N 100005
using namespace std;
//struct cmp{bool operator()(int a,int b){return a>b;}};
int main(){
	int n,lendown,lenup;
	int sum=0;
	int down[MAX_N]; 
	int up[MAX_N];
	int a[MAX_N];
	while(scanf("%d",&n)!=EOF){
		a[++sum]=n;
	}
	if(sum==0){
		cout<<"0"<<endl;
		cout<<"0"<<endl;
		return 0;
	}
	down[1]=a[1];
	up[1]=a[1];
	
	lendown=1;
	lenup=1;
//	for(int i=1;i<=sum;i++)cout<<a[i]<<" ";
	for(int i=2;i<=sum;i++){
		if(a[i]<=down[lendown])
			down[++lendown]=a[i];
		else{
			int j=upper_bound(down+1,down+lendown+1,a[i],greater<int>())-down;
//			cout<<j<<endl;
			down[j]=a[i];
		}
		if(a[i]>up[lenup])
			up[++lenup]=a[i];
		else{
			int j=lower_bound(up+1,up+lenup+1,a[i])-up;
			up[j]=a[i];
		}
	}
	cout<<lendown<<endl<<lenup<<endl;
//	for(int i=1;i<=lendown;i++){
//		cout<<down[i]<<" ";
//	}cout<<endl;
//		for(int i=1;i<=lenup;i++){
//		cout<<up[i]<<" ";
//	}cout<<endl;
	return 0;
}