【蓝桥杯国赛】游园安排

最长上升子序列 LIS 优化 二分与贪心 单调队列

数据范围 1e6,如果用普通的LIS会超时。
现在介绍优化版本:在单调队列中,保证所有字符串按照字典序单增。首先对题目进行字符串处理,按照顺序依次入队。
入队满足两个条件:

  • 当前元素比队尾元素大,直接入队;
  • 当前元素比队尾元素小,二分找到第一个大于等于该元素的元素,替换它。

最终的队列长度一定是该序列的最长子序列长度。(结论1)

在每一次入队操作时,同时更新len序列(表示以第i个元素结尾的序列的最长子序列的长度):

  • 当前元素比队尾元素大,长度为队列长度;
  • 当前元素比队尾元素小,二分找到第一个大于等于该元素的下标,长度为下标+1(当且仅当下标从0开始)。

在处理完这些操作后,我们得到了 len序列(回溯用) 以及 最终的 单调队列(没啥用)
因为题目要求输出最长子序列的每个元素,所以根据长度序列进行回溯,找到每个元素。
设最长子序列长度为 m ,有 m=q.size() ,见结论1.
从后往前判断以每个元素结尾,能得到的最长子序列长度,如果正好为当前m的值,记录一下当前元素为最长子序列中的元素。然后m--(因为已经又确定了一个元素)。直到m=0找完所有最长子序列中的元素。
倒序找,要倒序输出,越先找到的元素字典序越大,从小到大输出结果。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+100;
int main(){
    string s;
    cin>>s;
    vector<string> str;
    string tem;
    s+="A";
    str.push_back("*");
    tem+=s[0];
    for(int i=1;i<s.size();i++){
        if(s[i]>='A'&&s[i]<='Z'){
            str.push_back(tem);
            tem="";
            tem+=s[i];
        }else{
            tem+=s[i];
        }
    }
    vector<string> q;
    vector<int> len;
    q.push_back(str[1]);
    len.push_back(1);
    for(int i=2;i<str.size();i++){
        if(str[i]>q.back()){
            q.push_back(str[i]);
            len.push_back(q.size());
        }
        else{
            int l=0,r=q.size()-1;
            while(l<r){
                int mid=(l+r)>>1;
                if(q[mid]>=str[i]){
                    r=mid;
                }else{
                    l=mid+1;
                }
            }
            //int l=lower_bound(q.begin(),q.end(),str[i])-q.begin();
            q[l]=str[i];
            len.push_back(l+1);
        }
    }
    vector<string> res;
    for(int i=str.size()-1,m=q.size();m>0;i--){
        if(len[i]==m){
            res.push_back(str[i+1]);
            m--;
        }
    }
    for(int i=res.size()-1;i>=0;i--) cout<<res[i];
    return 0;
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值