最长上升序列 LIS 思路 以及细节

文章介绍了如何利用贪心思想和二分搜索算法解决寻找给定数列中最长不上升子序列的问题。通过维护一个单调递减的low数组,不断更新最长不上升子序列的长度。二分搜索优化了查找过程,将时间复杂度降低到O(nlogn)。
摘要由CSDN通过智能技术生成

提供一个好题: 

导弹拦截

题目意思:给定一个数列 ,问:

  1. 它的最长不上升子序列长度;(不上升和上升是有区别的)
  2. 最少能被划分成多少个不上升子序列。

LIS(最长上升子序列)模板:(以单调递增,最长上升序列中没有相同的数为例)

         贪心思想!!!

  • low数组的每一个下标都是一个栈为存储递减序列,栈顶(low数组下标下的数)
  • 当遍历到的数t大于栈顶 ,开创下一个栈,并存入。(数更小时时,要放到栈顶,覆盖之前的数,贪心思想:前边数越小,后边存的数才可能越多!!)
  • 当要存入的数t小于当前栈顶,搜索找到所有已经开的栈顶的数大于t的第一个栈中(栈顶的数在所有的栈中是单调递增的),这个操作的理解有难度,以一张图体会其作用:

 

 

二分搜索优化后为O(nlogn)


 
const int maxn =300003, INF = 0x7f7f7f7f;
int low[maxn], a[maxn];
int n, ans;
 
int binary_search(int *a, int R, int x)
//二分查找,返回a数组中第一个>=x的位置 
{
    int L = 1, mid;
    while(L <= R)
    {
        mid = (L+R) >> 1;
        if(a[mid] <= x)
            L = mid + 1;
        else 
            R = mid - 1;
    }
    return L;
}
 
int main()
{
    scanf("%d", &n);
    for(int i=1; i<=n; i++) 
    {
        scanf("%d", &a[i]); 
        low[i] = INF;   //由于low中存的是最小值,所以low初始化为INF 
    }
    low[1] = a[1]; 
    ans = 1;   //初始时LIS长度为1 
    for(int i=2; i<=n; i++)
    {
        if(a[i] > low[ans])    //若a[i]>=low[ans],直接把a[i]接到后面 
            low[++ans] = a[i];
        else       //否则,找到low中第一个>=a[i]的位置low[j],用a[i]更新low[j] 
            low[binary_search(low, ans, a[i])] = a[i];
    }
    printf("%d\n", ans);   //输出答案 
    return 0;
}


例题源代码 :

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stack>
#include <stdlib.h>
#include <iostream>
#include <math.h>
#include <queue>
#include <algorithm>
#define N 3000050
using namespace std;
int high[100010] = { 0 };
int low[100010] = { 0 };
int num = 1;
int  search(int i,int j,int x) {
	int l = i, r = j;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (high[mid] >= x)
			l= mid+1;
		else r= mid-1 ;
	}
	return l;
}
int  search2(int i, int j, int x) {
	int l = i, r = j;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (low[mid] <x)
			l = mid + 1;
		else r = mid - 1;
	}
	return r+1;
}
int main() {
	int t[100010];
	int n = 1;
	while (scanf("%d", &t[n])!=EOF) {
		n++;
	}
	int len = 1;
	high[len] = t[1];
	for (int i = 2; i <= n-1; i++) {
		if (high[len] >= t[i])high[++len] = t[i];

		else  high[search(1,len,t[i])] = t[i];
	}	
	printf("%d\n", len);
	low[num] = t[1];
	for (int i = 2; i <= n - 1; i++) {
		if (low[num] < t[i])low[++num] = t[i];
		else low[search2(1, num, t[i])]=t[i];
	}
	printf("%d", num);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

El.十一

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值