• UVa 1471 防线 set/LIS


    题意:给出长度为n的序列,要求你删除掉一段的连续子序列,使得剩下的序列的递增子序列最长
    分析:
    书上讲解的很详细,摘一段:
    最容易想到的算法是枚举j和i(前提是A[j]< A[i],否则拼不起来),然后分别往左和往 右数一数最远能延伸到哪里。枚举量为O(n2),而“数一数”的时间复杂度为O(n),因此总时间 复杂度为O(n3)。
    加上一个预处理,就能避免“数一数”这个过程,从而把时间复杂度降为O(n2)。设f(i)为 以第i个元素开头的最长L序列长度,g(i)为以第i个元素结尾的最长L序列长度,则不难在O(n) 时间内求出f(i)和g(i),然后枚举完j和i之后,最长L序列的长度就是g(j)+f(i)。
    还可以做得更好:只枚举i,不枚举j,而是用其他方法快速找一个j< i,使得A[j]< A[i], 且g(j)尽量大。如何快速找到呢?首先要排除一些肯定不是最优值的j。例如,若有j’满足A[j’] <=A[j]且g(j’)> g(j),则j肯定不满足条件,因为j’不仅是一个更长的L序列的末尾,而且它更容 易拼成。
    这样,把所有“有保留价值”的j按照A[j]从小到大排成一个有序表(根据刚才的结 论,A[j]相同的j只保留一个),则g也会是从小到大排列。那么用二分查找找到满足A[j]< A[i] 的最大的A[j],则它对应的g(j)也是最大的。
    不过这个方法只有当i固定时才有效。实际上每次计算完一个g(i)之后,还要把这个A[i] 加到上述有序表中,并且删除不可能是最优的A[j]。因为这个有序表会动态变化,无法使用 排序加二分查找的办法,而只能使用特殊的数据结构来满足要求。幸运的是,STL中的set就 满足这个要求——set中的元素可以看成是排好序的,而且自带lower_bound和upper_bound函 数,作用和之前讨论过的一样。

    #include<cstdio>
    #include<set>
    #include<algorithm>
    using namespace std;
    const int N=2e5+9;
    #define a first
    #define l second
    #define mp make_pair
    typedef pair<int,int>pii;
    
    int l[N],r[N],g[N],a[N];
    set<pii>s;
    int main()
    {
        //freopen("f.txt","r",stdin);
        int T;scanf("%d",&T);
        while(T--){
            int n;scanf("%d",&n);
            for(int i=1;i<=n;i++)scanf("%d",&a[i]);
            l[1]=1;
            for(int i=2;i<=n;i++)l[i]=a[i-1]<a[i]?l[i-1]+1:1;
            r[n]=1;
            for(int i=n-1;i>=1;i--)r[i]=a[i]<a[i+1]?r[i+1]+1:1;
            s.clear();
            s.insert(pii(a[1],l[1]));
            int ans=1;
            for(int i=2;i<=n;i++){
                pii t=mp(a[i],l[i]);
                set<pii>::iterator it=s.lower_bound(t);
                bool keep=1;
                if(it!=s.begin()){
                    pii last=*(--it);
                    ans=max(ans,last.l+r[i]);
                    if(last.l>=t.l)keep=0;
                }
                if(keep){
                    s.erase(t);
                    s.insert(t);
                    it=s.find(t);
                    ++it;
                    while(it!=s.end()&&it->a >t.a&&it->l <=t.l)s.erase(it++);
                }
            }
            printf("%d
    ",ans);
        }
        return 0;
    }

    这题还是LIS的变形,用LIS的nlogn的算法就可以解决,思想上和上面的分析一样,LIS稍微变化即可。其中g保存的是最优的a[i]的值(且它的l[i]值是最优的),可以看出g中保存的值是递增的。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=2e5+9;
    
    #define INF 0x3f3f3f3f;
    int l[N],r[N],g[N],a[N];
    int main()
    {
        //freopen("f.txt","r",stdin);
        int T;scanf("%d",&T);
        while(T--){
            int n;scanf("%d",&n);
            for(int i=1;i<=n;i++)scanf("%d",&a[i]);
            l[1]=1;
            for(int i=2;i<=n;i++)l[i]=a[i-1]<a[i]?l[i-1]+1:1;
            r[n]=1;
            for(int i=n-1;i>=1;i--)r[i]=a[i]<a[i+1]?r[i+1]+1:1;
            for(int i=0;i<=n;i++)g[i]=INF;
            int ans=0;
            for(int i=1;i<=n;i++){
                int len=lower_bound(g+1,g+1+n,a[i])-(g+1)+r[i];
                ans=max(ans,len);
                g[l[i]]=min(a[i],g[l[i]]);
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    使用Boost::ptime构建高精度计时器
    static和extern
    通用js地址选择器
    js模拟抛出球运动
    前端用Webpact打包React后端Node+Express实现简单留言版
    webpack 打包一个简单react组件
    img及父元素(容器)实现类似css3中的background-size:contain / background-size:cover
    通用js函数集锦<来源于网络> 【二】
    通用js函数集锦<来源于网络/自己> 【一】
    向上滚动或者向下滚动分页异步加载数据(Ajax + lazyload)[上拉加载组件]
  • 原文地址:https://www.cnblogs.com/01world/p/5651208.html
Copyright © 2020-2023  润新知