zoukankan      html  css  js  c++  java
  • Luogu 3402 最长公共子序列(二分,最长递增子序列)

    Luogu 3402 最长公共子序列(二分,最长递增子序列)

    Description

    经过长时间的摸索和练习,DJL终于学会了怎么求LCS。Johann感觉DJL孺子可教,就给他布置了一个课后作业:

    给定两个长度分别为n和m的序列,序列中的每个元素都是正整数。保证每个序列中的各个元素互不相同。求这两个序列的最长公共子序列的长度。

    DJL最讨厌重复劳动,所以不想做那些做过的题。于是他找你来帮他做作业。

    Input

    第一行两个整数n和m,表示两个数列的长度。
    第二行一行n个整数$$a_1,a_2,…,a_n,保证1≤a_i≤10^9$$。
    第三行一行m个整数$$b_1,b_2,…,b_m,保证1≤b_i≤10^9$$。

    对于40%的数据,n, m≤3000
    对于100%的数据,n, m≤300000

    Output

    一行一个整数,表示两个数列的最长公共子序列的长度。

    Sample Input

    6 6
    1 3 5 7 9 8
    3 4 5 6 7 8

    Sample Output

    4

    Http

    Luogu:https://www.luogu.org/problem/show?pid=3402

    Source

    二分

    解决思路

    这道题的弱化版在这里

    因为数据加强了,我们不能使用弱化版中的动态规划方法了。

    题目中给出了元素互不相同的条件。我们可以将求最长公共子序列(LCS)转化为求最长递增子序列(LIS)

    读入A数组的时候,我们定义一个Map[x]表示数x对应的编号,Map[x]=i;这样就相当于把数组A变成了一个递增的序列。

    如果我们把B数组里的数都按照A中的规则转置一下,这个题目就变成求B的最长递增子序列了,具体操作如下:

    然后在处理B数组的时候(假设我们现在处理数字x),先让x=Map[x]如果此时x==0则说明原来的数字x并没有在数组A中出现过,所以自然也不会成为最长公共子序列的解,直接舍去即可。

    这时我们将找到的最长递增子序列放入一个vector(这里用Arr表示),并保证其有序。若x(这个x是用Map转置后的)比vector里所有元素都大(即比Arr[Arr.size()-1]大),则直接将其放到队尾;否则二分查找第一个比x大的元素并替换之。

    相信你也发现了,这个算法的核心思路是贪心,我们可以看到,它是让Arr中的元素尽可能的小,以此来求出最长递增子序列,也就可以求出原题的最长公共子序列。

    需要注意的是,用这种算法求最长递增子序列只能求出其长度,Arr中的数不一定就是最后要求的最长递增子序列。(为什么呢?仔细想一想)

    PS:这个题目真心坑爹,不仅要用上面的这个算法,读入还必须用getchar()读入优化,就连scanf都会超时,真是……

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<map>
    using namespace std;
    
    int n,m;
    map<int,int> Map;
    vector<int> Arr;
    
    int read();//读入优化,一定要打
    
    int main()
    {
        int x;
        n=read();
        m=read();
        for (int i=1;i<=n;i++)
        {
            //scanf("%d",&x);
            Map[read()]=i;
        }
        for (int i=1;i<=m;i++)//将LCS问题转换为LIS问题,降低时间复杂度
        {
            //scanf("%d",&x);
            x=Map[read()];
            if (x==0)
                continue;
            if ((Arr.size()==0)||(x>Arr[Arr.size()-1]))//如果比其中的都大,直接放到后面
                Arr.push_back(x);
            else//否则,替换第一个比它大的
                *lower_bound(Arr.begin(),Arr.end(),x)=x;
        }
        cout<<Arr.size()<<endl;//Arr的大小就是最后最长子序列的大小
        return 0;
    }
    
    int read()
    {
        int x=0;
        int k=1;
        char ch=getchar();
        while (((ch<'0')||(ch>'9')) &&(ch!='-'))
            ch=getchar();
        if (ch=='-')
        {
            k=-1;
            ch=getchar();
        }
        while ((ch<='9')&&(ch>='0'))
        {
            x=x*10+ch-48;
            ch=getchar();
        }
        return x*k;
    }
    
    自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。
  • 相关阅读:
    NSIndexPath 延伸
    iOS进阶推荐的书目
    配置App真机测试证书的流程 一览
    NSAttributedString 的21种属性 详解
    KVO & 通知 小记
    贝塞尔曲线 & CAShapeLayer & Stroke 动画 浅谈
    提升开发人员修为的探讨
    热门IOS 第三方库
    drawRect & 内存 -> 深究
    CALayer & UIView 关系浅析
  • 原文地址:https://www.cnblogs.com/SYCstudio/p/7137870.html
Copyright © 2011-2022 走看看