lis(贪心 + 二分优化)

时间复杂度:(nlogn)

这种优化的主要思想是贪心,对于一串最大上升子序列,其结尾元素越小对答案的贡献越大,也就是说,其结尾元素越小就越有可能在其末尾接上新的数。

具体做法的话,我们会使用一个dp数组存储长度为 i 的lis的结尾的最小值。对于dp数组,我们分别对每一个元素进行判断,若当前元素大于dp数组结尾的元素,我们就将该元素插入dp数组的末尾,数组长度加一;若当前元素小于dp数组的结尾元素,我们就使用二分查找,在已有的dp数组中寻找到第一个大于等于当前元素的数,使用当前元素将其替换掉,这样可以保证dp数组里存储lis的数据尽可能的小,这样就越有利于保证越可能在lis末尾插入新元素。

例:

对于一个数组arr:
arr[] = 2 5 18 3 4 9 15
dp[1] = 2;
5大于2,所以dp[2] = 5
18大于5,所以dp[3] = 18
3小于18,所以使用二分查找,查找到的角标是2,所以更新dp[2] = 3
4小于18,所以使用二分查找,查找到的角标是3,所以更新dp[3] = 4
9大于4,所以dp[4] = 9
15大于9,所以dp[5] = 15

所以该数组的lis就是5

这里值得注意的一件事是:通过这种方法找到的lis并不一定是真正的lis,只是能够保证其长度和真正的lis相同,因为这种操作的贪心思想会导致数组中较小的数会把dp数组中较大的数替换掉。

如: 1 3 4 6 7 2

其真正的lis应该是:1 3 4 6 7,但通过这种方法找出来的lis却是:1 2 4 6 7。

代码模板:

# include<iostream>
# include<string.h>
# include<algorithm>

using namespace std;

const int maxn = 5e5 + 5;

int dp[maxn];
int arr[maxn];

int main(){
	int n;
	int len = 1;
	int ans = 1;
	cin >> n;
	memset(dp, 0, sizeof(dp));
	for(int i = 1; i <= n; i++){
		cin >> arr[i];
	}	
	dp[1] = arr[1];
	for(int i = 2; i <= n; i++){
		if(arr[i] > dp[len])
			dp[++len] = arr[i];
		else if(arr[i] < dp[len]){
			int temp = lower_bound(dp + 1, dp + len + 1, arr[i]) - dp;
			dp[temp] = arr[i];
		}
	}
	cout << len << endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值