最長上升子序列dpO(n^2) 和 O(nlgn)

題目地址
描述
給定一個長度爲N的數列,求數值嚴格單調遞增的子序列的長度最長是多少。

輸入格式
第一行包含整數N。

第二行包含N個整數,表示完整序列。

輸出格式
輸出一個整數,表示最大長度。

數據範圍
1≤N≤1000,
−109≤數列中的數≤109
輸入樣例:
7
3 1 2 1 8 5 6
輸出樣例:
4

法1: 用f[i] 表示 數列中以i爲結尾的最長嚴格單調遞增的子序列的最長長度,則有當 a[i] > a[j] {j 爲 0~i-1 } 中的一個數時 ,有f[i] = max(f[i],f[j] + 1),相當於可以把a[i] 這個數 接到 a[j] 後面得到的長度。
故有,每次遍歷到第i位置的時候,都可以往前面遍歷找到一個比他小的,更新f。
c++,代碼如下:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
 
const int INF = 0x3f3f3f3f;
const double eps = 1e-5;
const int mod = 1e9+7;
const int N = 1010;

//n^2 
//f[i] 存當前i號位置爲尾最長上升序列 
int a[N],f[N];

int main(){
    int n; cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++)
	{
		f[i] = 1;
		for(int j=1;j<i;j++)//枚舉,比它小的都取max 
			if(a[i] > a[j])
				f[i] = max(f[i],f[j] + 1);
	}
	
	int res = 0;
	for(int i=1;i<=n;i++) res = max(res,f[i]);
	cout<<res<<endl;
	
	return 0;
}

方法2:用lower_bound將當前的a[i]修改到一個已經構建上升序列中(開始爲空)。{a[lower_bound(a,a+len+1,a[i])-a] = a[i]},最大長度就是最長上升子序列。理由: 若當前的a[i]是最大的,則會被加入到最後。若不是最大的,則會更新上升序列中的某個值。 會不會有影響??? 不會。對於一個已經形成了的上升序列,後面的值接到末尾,本身就無影響。 當把序列中的某個數更新後,最大長度沒變,序列元素改變,整體不是一個子序列,但可以把當前更新的元素近似改變爲原來的那個數,就很好理解了。更新是爲了後序序列更好處理,因爲結尾越小,後面的值就越容易修改。爲什麼不是插入呢?而是修改,如果是插入,則會造成不是一個子序列。
(講的繞,看看例子吧)

例如: 1 2 4 7 3 5 6 9 8

1
1 2
1 2 4
1 2 4 7
1 2 3 7 -> 此時應該把1->2->3看成一個真正子序列,1->2->3 == 4->7近似看成上一個子序列,假設是插入的話 1 2 3 4 7,這不是子序列了.
1 2 3 5
1 2 3 5 6
1 2 3 5 6 9
1 2 3 5 6 8
故爲6
c++代碼如下:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const double eps = 1e-4;
const int mod = 100003;
const int N = 1000010;

int q[N];

int main(){
	int n; cin>>n;
	q[0] = -2e9; int len = 0;
	for(int i=0;i<n;i++)
	{
		int x; cin>>x;
		int pos = lower_bound(q,q+len+1,x) - q;
		len = max(len,pos);
		q[pos] = x;
	}
	
	cout<<len<<endl;
	
    return 0;
}

有錯誤還請斧正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章