最大上升子序列

最大上升子序列

  1. LIS定义
    最长上升子序列(Longest Increasing Subsequence),简称LIS,也有些情况求的是最长非降序子序列,二者区别就是序列中是否可以有相等的数。假设我们有一个序列 b i,当b1 < b2 < … < bS的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2, …, aN),我们也可以从中得到一些上升的子序列(ai1, ai2, …, aiK),这里1 <= i1 < i2 < … < iK <= N,但必须按照从前到后的顺序。比如,对于序列(1, 7, 3, 5, 9, 4, 8),我们就会得到一些上升的子序列,如(1, 7, 9), (3, 4, 8), (1, 3, 5, 8)等等,而这些子序列中最长的(如子序列(1, 3, 5, 8) ),它的长度为4,因此该序列的最长上升子序列长度为4。
    简单来说,就是按照它所提供的顺序寻找最大的升序数列。
  2. 动态规划解法
    动态规划的思路是不断回溯,即这一阶段的答案可以由上一阶段推出;
    这一题我们引入f【i】数组储存以第i个数结尾的最大升子序列的长度;
    例如数列d{1,7,3,5,9,4,8},它的f数组为{1,2,2,3,4,3,4}
    如何求得f【i】呢,我们只需将d【i】与它前面的每一个作比较,如果找到d【i】大于d【j】,即可以认为存在一个以d【i】结尾的升子序列,其f【i】=f【j】+1;但要注意的是,这不一定是以它结尾的最大升子序列,d【i】之前的数出现一个比它小的数,就可以产生一个f【i】,我们只需取max,就能得到最大的f【i】。当我们求出了每一个数的f时,f数组的最大值就是整个数列的最大上升子序列长度了。
    代码:
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b[10000],f[10000],x=1;
cin>>a;
for(int i=0;i<a;i++){
	cin>>b[i];
	f[i]=1;
}
for(int i=0;i<a;i++){
	for(int j=0;j<i;j++){
		if(b[j]<b[i]){
			f[i]=max(f[i],f[j]+1);
		}
	}
}
for(int i=0;i<a;i++){
	x=max(x,f[i]);
}
cout<<x;
}

  1. 二分+贪心解法
    第一种暴力解法运用了两重循环,时间复杂度为O(nn),而这种算法可以降低为O(nlgn),在这里我们引入一个c数组,c【i】表示长度为i的LIS结尾元素的最小值,我们只需遍历一遍原始数组b,如果b【i】>c【j】,(j为c数组的长度,c【j】为末尾元素)将b【i】接到c【j】后面,如果b【i】<c【j】,我们可以用二分查找找到c中第一个比b【i】大的数,用b【i】将其替换(这里用到了贪心,想要序列尽可能长,序列要尽可能小),这种操作尽管打乱了升序列(最后c中的序列不是b的升序列)但c的长度就是b最大升序列的长度,即中间替换的做法既维持了c长度的不变,又保证了继续遍历时可以达到最优,举个例子:有数组b{1,4,8,10,5,6,7},当我们遍历到10时,c为{1,4,8,10},之后遍历到5,依据之前算法,我们用5替换8,这时,我们遍历完b之后,c变成了{1,4,5,6,7},在这里,将较小的5替换较大的8,保证了之后6,7(比5大比8小)的数的顺利插入**(这个例子恰好c为b的最大升子序列,而实际情况下大多数c不是,可以自行举例)**
    再次强调 这个算法的核心在于1.c【i】表示长度为i的LIS结尾元素的最小值。2.遍历完成后c的长度就是最大升子序列的长度。
    代码:
#include <iostream>
using namespace std;
int binary_search(int *a,int r,int x){
   int l=0,mid;
   while(l<=r){
   	mid=(l+r)>>1;
   	if(a[mid]<=x){
   		l=mid+1;
   	}
   	else r=mid-1;
   }
   return l;
}//二分查找返回数组下标
int main(){
   int a,b[1000],c[1000],len;
   cin>>a;
   for(int i=0;i<a;i++){
   	cin>>b[i];
   	c[i]=0;
   }
   c[0]=b[0];
   len=1;
   for(int i=1;i<a;i++){
   	if(b[i]>c[len]){
   		c[++len]=b[i];
   	}
   else
      	c[binary_search(c,len,b[i])]=b[i];
   }
   cout<<len;
}```
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值