【蓝桥杯】LIS

一.LIS问题

最长上升子序列(Longest  Increasing Subsequence)

1.子串和子序列

     (1)字符子串指的是字符串中连续的n个字符。

     (2)字符子序列指的是字符串中不一定连续但先后顺序一致的n个字符,即可以去掉字符串中的部分字符,但不可改变其前后顺序。

2.问题求解

2.1 DP  O(n^2):

问题分析:

数组d[i]表示,以A[i]结尾的最长上升子序列的长度。

d[i]=\begin{cases} & d[j]+1 \quad A[j]<A[i]\quad and \quad j< i \quad and \quad 0\leq j\leq i-1 \\ &1 \quad otherwise\\ \end{cases}

我们通过这种方式是无法求出最长上升子序列具体是什么,这点和最长公共子序列不同。

代码实现:

//LIS DP
#include <iostream>

using namespace std;

const int N=1e4+2;
int n,d[N],result=0;
long long a[N];

int main(int argc, const char * argv[]) {
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n;
    
    for(int i=0;i<n;i++){
        cin>>a[i];
        int j=i-1,flag=1;
        while(j>=0){
            if(a[j]<a[i]){
                d[i]=d[j]+1;
                flag=0;
                break;
            }
            --j;
        }
        if(flag){
            d[i]=1;
        }
    }
    for(int i=0;i<n;i++){
        if(result<d[i]){
            result=d[i];
        }
    }
    cout<<result;
    return 0;
}

2.2  贪心+二分 O(nlogn):

问题分析:

low [ i ]表示长度为i的序列结尾元素的最小值。

每扫描一个新的元素,我们需要判断a[i]与low[max]的大小,如果a[i]大于low[max],则low[++max]=a[i];如果a[i]小于low[max],从max向前回溯,找到第一个a[i]小于low[j]的j值,low[j]=a[i],重复上述操作,直至扫描完整个序列。

注意在回溯过程中,由于low [ i ]记录的是元素的最小值,那么low数组是一个有序数组,则可以利用二分查找,二分一次 low 数组的时间复杂度为O(logn),所以总的时间复杂度是O(nlogn)。

代码实现:

//LIS 贪心+二分
#include <iostream>

using namespace std;

const int N=1e4+2;
long long a[N],low[N]={0};
int n,result=0;

int main(int argc, const char * argv[]) {
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n;
    
    for(int i=0;i<n;i++){
        cin>>a[i];
        if(a[i]>low[result]){
            low[++result]=a[i];
        }
        else{
            int end=result-1,first=1;
            int mid=(first+end)/2;
            while(first<=end){
                mid=(first+end)/2;
                if(a[i]>low[mid]){
                    first=mid+1;
                }
                else{
                    end=mid-1;
                }
            }
            if(mid>1){
                low[mid]=a[i];
            }
        }
    }
    
    cout<<result;
    return 0;
}

 

 注:

 lower_bound()、upper_bound()、equal_range() 以及 binary_search() ,它们的底层实现采用的都是二分查找的方式,进行查找的数组需要有序,使用时均需 #include <algorithm>

1) lower_bound() 不小于

当查找成功时,迭代器指向找到的元素;反之,如果查找失败,迭代器的指向和 last 迭代器相同。

语法格式: 

//在 [first, last) 区域内查找不小于 val 的元素
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,
                             const T& val);
//在 [first, last) 区域内查找第一个不符合 comp 规则的元素
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,
                             const T& val, Compare comp);

 具体演示:

//lower_bound()
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;


int main(int argc, const char * argv[]) {
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    
    int a[5]={1,2,3,4,5};
    int *p=lower_bound(a, a+5, 3);
    //从a数组找到第一个不小于3的元素
    return 0;
}

2)upper_bound() 大于

//查找[first, last)区域中第一个大于 val 的元素。
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,
                             const T& val);
//查找[first, last)区域中第一个不符合 comp 规则的元素
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,
                             const T& val, Compare comp);

3)equal_range()

//找到 [first, last) 范围中所有等于 val 的元素
pair<ForwardIterator,ForwardIterator> equal_range (ForwardIterator first, ForwardIterator last, const T& val);

4)binary_search()

//查找 [first, last) 区域内是否包含 val
bool binary_search (ForwardIterator first, ForwardIterator last,
                      const T& val);
//根据 comp 指定的规则,查找 [first, last) 区域内是否包含 val
bool binary_search (ForwardIterator first, ForwardIterator last,
                      const T& val, Compare comp);

使用lower_bound()完成二分查找:

//贪心+二分 lower_bound()实现
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

const int N=1e4+10;
long long a[N],low[N]={0};
int result=0,n;
int main(int argc, const char * argv[]) {
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    cin>>n;
    for(int i=0;i<n;i++){
        cin>>a[i];
        if(a[i]>low[result]){
            low[++result]=a[i];
        }
        else{
            long long *p=lower_bound(low, low+result, a[i]);
            //找到第一个不小于a[i]的元素
            *p=a[i];
        }
    }
    cout<<result;
    return 0;
}

2.3 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值