动态规划之最长上升子序列(LIS模板)再整理

1.O(n^2)LIS模板

      dp[i]代表:    必须以a[i]为结尾的LIS的长度.
#include <iostream>
#include<cstring>
using namespace std;

const int nmax=1000+5;
int a[nmax];//存储数值序列
int dp[nmax];//dp[i]记录必须以a[i]为结尾的LIS的长度 

int main(int argc, char** argv) {
	int n; 
	while(cin>>n){
		for(int  i=0;i<n;i++){
			cin>>a[i];
			dp[i]=1;
		}
		int ans=1;//ans记录整个序列的LIS长度,初值为1 
		for(int i=1;i<n;i++){
			for(int j=0;j<i;j++){
				if(a[j]<a[i]){
					dp[i]=max(dp[j]+1,dp[i]);
				}
				ans=max(ans,dp[i]);
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}

2.1 O(nlogn)LIS模板(简化版——使用lower_bound)

      dp[i]代表: 假设当前遍历到某个数cur,遍历到当前时刻为止,长度为i+1的LIS的最小末尾

#include <iostream>
#include<cstring>
using namespace std;

const int nmax=1000+5;
int a[nmax];//存储数值序列
int dp[nmax];//dp[i]记录必须以a[i]为结尾的LIS的长度 
const int INF=0x3f3f3f;  

int main(int argc, char** argv) {
	int n; 
	while(cin>>n){
		for(int  i=0;i<n;i++){
			cin>>a[i];
			dp[i]=INF;//不要设置为1,因为更新时会产生dp[i]为1的情况 
		}
	for(int i=0;i<n;i++){
		*lower_bound(dp,dp+n,a[i])=a[i];//在dp数组位置0~n区间内找第一个大于a[i]的数,并用a[i]替换; 
	}
	cout<<lower_bound(dp,dp+n,INF)-dp<<endl;//找到第一个INF的地址减去首地址就是最大子序列的长度;
	}
	return 0;
}

2.2 O(nlogn)LIS模板(自己实现了二分查找的过程)

      dp[i]代表: 假设当前遍历到某个数cur,遍历到当前时刻为止,长度为i+1的LIS的最小末尾

#include <iostream>
#include<cstring>
using namespace std;

const int nmax=1000+5;
int a[nmax];//存储数值序列
int dp[nmax];//dp[i]记录遍历到当前时刻为止,长度为i+1的LIS的最小末尾 
//int ends[nmax];//有效区数组 

const int INF=0x3f3f3f;  

int main(int argc, char** argv) {
	int n; 
	while(cin>>n){
		for(int  i=0;i<n;i++){
			cin>>a[i]; 
		}
	    int* ends=new int[n];//有效区数组 
	    ends[0]=a[0];//直接拷贝数组中第一个元素 
	    dp[0]=1;
	    
	    int right=0;//有效区的右边界
		//在有效区里二分查找用到的变量 
	    int l=0;//左查找边界 
	    int r=0;//右查找边界 
	    int m=0;//中间查找的位置 
	    
	    for(int i=1;i<n;i++){//有效区插入时保证有序 
	    	l=0;
	    	r=right;
	    	while(l<=r){
	    		m=(l+r)/2;
				if(ends[m]<a[i]){
					l=m+1;//如果最终没找到,有效区右边界扩充1个单位 
				} 
				else{//ends[m]>=a[i]
					r=m-1;
				}
			}
			right=max(right,l);//如果没找到,有效区右边界扩充1个单位 
			                   //如果找到了,右边界仍然为right
			ends[l]=a[i];//若有效区中没找到大于a[i]的数,更新新扩充的右边界 
			             //若有效区中找到大于a[i]的数,将该数改写成a[i] 
			dp[i]=l+1;				    
		}
		
		int ans=1;
		for(int i=0;i<n;i++){//dp[i]已经记录了LIS的最小末尾,遍历一遍求最值 
			ans=max(ans,dp[i]);
		}
		cout<<ans<<endl;
    }
	return 0;
}


猜你喜欢

转载自blog.csdn.net/qian2213762498/article/details/79808733