动态规划: 最长上升子序列(LIS)

 

学习动态规划问题(DP问题)中,其中有一个知识点叫最长上升子序列(longes increasing subsequence),也可以叫最长非降序子序列,简称LIS。简单说一下自己的心得。

关于这个问题,之所以能用动态规划来解决,只因为其满足动态规划的条件,当前解可以由上一个阶段的解推出

建立状态转移方程,就能把大的问题转化为小的问题,进而逐渐缩小规模,直至解决。

分析:设有n个数的最长上升子序列长度为f(n),我们要求f(n),可以先求f(n-1),再对n做判断,反复判断,直至n=1,我们可以知道f(1)=1;

具体做法:a[n]储存n个数,维护一个一维数组dp[I],dp[I]表示前I个数的LIS,每加入一个数a[I],找出满足(j<I&&a[j]<a[I])的最大的dp[j],即找出当前能让a[I]加入的最长的序列,然后加一,即dp[I]=max(dp[I],dp[j]j<I&&a[j]<a[I])+1.

代码如下:(有重复元素)

int LIS(int a[], int n){                              //传入一个大小为n的数组,返回最长上升子序列长度,允许含相同元素
	int dp[n];
	for(int i = 0; i < n; i++){                   //初始化,还没判断时,对于每个dp【i】都是只有a【i】的长度为1的序列
		dp[i] = 1;
	}
	for(int i = 0; i < n; i++){                    //外循环遍历a【i】
		for(int j = 0; j < i; j++){            //内循环找出满足a[j]<a[i]的最大dp【i】
			if(a[j] < a[i]){
				dp[i] = max(dp[i], dp[j]) + 1;
			}
		}
	}
	int mmax = 0;
	for(int i = 0; i < n; i++){
		if(dp[i] > mmax){
			mmax = dp[i];
		}
	}
	return mmax;
} 

 

上述算法通过两个循环不难看出复杂度为O(n2),可以通过优化使复杂度降低到O(nlogn),方法时将内循环搜索改为二分查找,这样二分查找复杂度为logn,可以大大提高速度。但二分需有序,因此我们换一种dp数组的表示意义。

具体做法:同样是a[i]储存n个数,不同的是维护的数组dp1[i]表示的是LIS长度为i的序列最小的结尾值,最后输出dp数组的长度即为所求。比较见下表:

 

a[0]a[1]a[2]a[3]a[4]a[5]a[6]
1735948
dp[0]dp[1]dp[2]dp[3]dp[4]dp[5]dp[6]
223434
dp1[0]dp1[1]dp1[2]dp1[3]dp1[4]dp1[5]dp1[6]
1348000

代码如下:(有重复元素)

int lis(int a[], int n){
	int dp[N];
	dp[0] = a[0];
	int len = 0;
	for(int i = 1; i < n; i++){
		if(a[i] >= dp[len]){
			dp[++len] = a[i];
		}else{
			int k = upper_bound(dp, dp+len+1, a[i])-dp;
			dp[k] = a[i];
		}
	}
	return len+1;
}

 

(无重复元素):

int LIS(int a[], int n){
	int dp[N];
	dp[0] = a[0];
	int len = 0;
	for(int i = 1; i < n; i++){
		if(a[i] > dp[len]){
			dp[++len] = a[i];
		}else{
			int k = lower_bound(dp, dp+len+1, a[i])-dp;   
			dp[k] = a[i];
		}
	}
	return len+1;
}a[i] > dp[len]){
			dp[++len] = a[i];
		}else{
			int k = lower_bound(dp, dp+len+1, a[i])-dp;   
			dp[k] = a[i];
		}
	}
	return len+1;
}

 

注意有无重复元素代码的红色部分。

upper_bound与lower_bound的比较
首先,最形象的一句话:
upper_bound(i) 返回的是键值为i的元素可以插入的最后一个位置(上界)
lowe_bound(i) 返回的是键值为i的元素可以插入的位置的第一个位置(下界)。
举例说明
1 2 4 5 中插入3都是返回位置3
1 3 3 4 中插入3upper_bound就返回位置4而lower_bound返回的是位置2
 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值