最长上升子序列的两种复杂度解法

动态规划o( N 2 N^2 N2)的解法
  • 我们先确定状态:
    dp[k]:表示以 a k a_k ak为终点的最长上升子序列的长度。可以看到,该状态满足无后效性
  • 然后确定递推式
    初始状态:maxLen(1)=1
    maxLen(k)=max {maxLen(i):1<=i<k, a i < a k a_i<a_k ai<ak,且k!=1}+1
    如果找不到这样的i,那么有maxLen(k)=1
  • 代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1010;
int a[MAXN];
int maxLen[MAXN],N;
int main(){
    
    
	int i,j,k;
	cin>>N;
	for(i=1;i<=N;i++){
    
    
		cin>>a[i];
		maxLen[i]=1;
	}
	for(i=2;i<=N;i++){
    
    
		for(j=1;j<i;j++){
    
    
			if(a[i]>a[j]){
    
    
				maxLen[i]=max(maxLen[i],maxLen[j]+1);
			}
		}
	}
	//利用C++的max_element函数求数组的最大值 
	cout<<*max_element(maxLen+1,maxLen+N+1)<<endl;
}

贪心+二分o( N l o g N Nlog_N NlogN)的解法
  • 这种解法是个一种叫做纸牌接龙的游戏相关,感兴趣的可以自己搜索一下,我只说下算法操作的步骤。
  1. 维护一个递增的数组B,如果A[i]大于数组B中的所有元素,则将其放到数组B的后面
  2. 否则我们用lower_bound()函数找到第一个大于等于A[i]的数,并用A[i]将此数替换掉
  • 这个算法用到了贪心的思想,使数组B中的数尽可能的小,以便于后面的数更容易的进来。要注意的是,数组B中的数并不是LIS序列,只是数组的长度等于LIS的长度。至于怎么求LIS序列,后面我将讲到。
  • 代码
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
#define scf scanf
#define prf printf
const ll MAX_N=1e5+7;
ll T,N;
ll A[MAX_N],B[MAX_N];
ll do_LIS(){
    
    
	ll i,j,k,cnt=0;
	for(i=0;i<N;i++){
    
    
		ll L=0,R=cnt-1,mid,res=-1;
		while(L<=R){
    
    
			mid=L+(R-L)/2;
			if(B[mid]>=A[i]){
    
    
				res=mid;
				R=mid-1;
			}
			else{
    
    
				L=mid+1;
			}
		}
		if(res==-1){
    
    //B数组中的所有数都小于A[i]
			B[cnt]=A[i];
			cnt++;
		}
		else{
    
    
			B[res]=A[i];
		}
	}
	return cnt;
}
int main()
{
    
    
	ll i,j,k=0;
	scf("%lld",&N);
	for(i=0;i<N;i++){
    
    
		scf("%lld",&A[i]);
	}
	ll res=do_LIS();
	prf("%lld\n",res);
	return 0;
}
  • C++的lower_bound()函数版本
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
#define scf scanf
#define prf printf
const ll MAX_N=1e5+7;
ll T,N;
ll A[MAX_N],B[MAX_N];
ll do_LIS(){
    
    
	ll i,j,k,cnt=0;
	for(i=0;i<N;i++){
    
    
		ll pos=lower_bound(B,B+cnt,A[i])-B;
		if(pos==cnt){
    
    
			cnt++;
		}
		B[pos]=A[i];
	}
	return cnt;
}
int main()
{
    
    
	ll i,j,k=0;
	scf("%lld",&N);
	for(i=0;i<N;i++){
    
    
		scf("%lld",&A[i]);
	}
	ll res=do_LIS();
	prf("%lld\n",res);
	return 0;
}
  • 前面说了, 数组B存放的并不是LIS的序列,此时需要一个数组index[],记录数组A[i]在数组B中位置,然后倒序查找输出
  • 代码
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
#define scf scanf
#define prf printf
const ll INF=0x3f3f3f3f;
const ll MAX_N=1e5+7;
ll T,N;
ll A[MAX_N],B[MAX_N],index[MAX_N];
vector<ll>V;
ll do_LIS(){
    
    
	ll i,j,k,cnt=0;
	for(i=0;i<N;i++){
    
    
		ll pos=lower_bound(B,B+cnt,A[i])-B;
		if(pos==cnt){
    
    
			cnt++;
		}
		B[pos]=A[i];
		index[i]=pos;
	}
	return cnt;
}
int main()
{
    
    
	ll i,j,k=0;
	scf("%lld",&N);
	for(i=0;i<N;i++){
    
    
		scf("%lld",&A[i]);
	}
	ll res=do_LIS();
	prf("%lld\n",res);
	ll maxx=INF,cnt=res-1;
	for(i=N-1;i>=0;i--){
    
    //反向存的 
		if(cnt<0)
		break;
		if(index[i]==cnt&&A[i]<maxx){
    
    
			V.push_back(A[i]);
			cnt--;
			maxx=A[i];
		}
	}
	reverse(V.begin(),V.end());
	for(i=0;i<V.size();i++){
    
    
		prf("%lld ",V[i]);
	}
	return 0;
}

最长下降子序列

其实就是将数组反过来求出最长上升子序列的长度。


最长不下降子序列

只需要将求LIS的语句稍加修改就可以了

  • 动态规划版本
    将A[i]>A[j]----->A[i]>=A[j]
  • 代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1010;
int a[MAXN];
int maxLen[MAXN],N;
int main(){
    
    
	int i,j,k;
	cin>>N;
	for(i=1;i<=N;i++){
    
    
		cin>>a[i];
		maxLen[i]=1;
	}
	for(i=2;i<=N;i++){
    
    
		for(j=1;j<i;j++){
    
    
			if(a[i]>=a[j]){
    
    
				maxLen[i]=max(maxLen[i],maxLen[j]+1);
			}
		}
	}
	//利用C++的max_element函数求数组的最大值 
	cout<<*max_element(maxLen+1,maxLen+N+1)<<endl;
}
  • 贪心+二分版本
    将lower_bound()函数改为upper_bound()函数,在数组B中遇到等于A[i]的情况,我们不将其覆盖。
  • 代码
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
#define scf scanf
#define prf printf
const ll MAX_N=1e5+7;
ll T,N;
ll A[MAX_N],B[MAX_N];
ll do_LIS(){
    
    
	ll i,j,k,cnt=0;
	for(i=0;i<N;i++){
    
    
		ll pos=upper_bound(B,B+cnt,A[i])-B;
		if(pos==cnt){
    
    
			cnt++;
		}
		B[pos]=A[i];
	}
	return cnt;
}
int main()
{
    
    
	ll i,j,k=0;
	scf("%lld",&N);
	for(i=0;i<N;i++){
    
    
		scf("%lld",&A[i]);
	}
	ll res=do_LIS();
	prf("%lld\n",res);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43311695/article/details/108588934