zoukankan      html  css  js  c++  java
  • PKU 2823 Sliding Window(线段树||RMQ||单调队列)

    题目大意:原题链接(定长区间求最值)

    给定长为n的数组,求出每k个数之间的最小/大值。

    解法一:线段树

    segtree节点存储区间的最小/大值

    Query_min(int p,int l,int r,int ll,int rr)从编号为p的节点开始在区间[l,r]内查询区间[ll,rr]的最小值

    Query_max(int p,int l,int r,int ll,int rr)从编号为p的节点开始在区间[l,r]内查询区间[ll,rr]的最大值

    当[l,r]区间完全包含于[ll,rr]区间时,直接return segmin[p]或者return segmax[p].

    注意:

    1.这两个递归调用时,传入的参数ll和rr始终没有变;

    2.这两个函数中的if(rr>mid)不能取"=",比如在区间[1,8]内查询区间[1,4]的最值,因为if(ll<=mid)已经把区间[1,4]的结果给计算出来了,如果加上"=",那么接下来就要在区间[5,8]内查询区间[1,4]的最值了,而区间[1,4]始终<区间[5,8]内的mid,导致程序无法return,崩溃;

    3.这两个函数中的int begin=inf;int end=inf;语句和int begin=-inf;int end=-inf语句很关键,比如在区间[1,8]查询内查询区间[1,5]的最值,因为1<=1<=4<=4如上所述区间[1,4]完全包含于区间[1,4];所以区间[1,4]的最值正常返回给begin或者end,但是如果每次递归调用时不重新给begin和end赋值的话,那么叶子节点[5]的值无法正常返回,因为叶子节点[5]返回的值无法和begin或者end比较大小.因为begin和end根本就没有值.

    #include<cstdio>
    #include<algorithm>
    #define maxn 1000005
    #define inf 0x3f3f3f3f
    using namespace std;
    int Segmin[maxn<<2],Segmax[maxn<<2];
    int n,k,value;
    
    void Build(int p,int l,int r)
    {
        if(l==r){
            scanf("%d",&value);
            Segmax[p]=Segmin[p]=value;
            return;
        }
        int mid=(l+r)/2;
        Build(2*p,l,mid);
        Build(2*p+1,mid+1,r);
        Segmin[p]=min(Segmin[2*p],Segmin[2*p+1]);
        Segmax[p]=max(Segmax[2*p],Segmax[2*p+1]);
    }
    int Query_min(int p,int l,int r,int ll,int rr)
    {
        if(ll<=l&&r<=rr)
            return Segmin[p];
        int mid=(l+r)/2;
        int begin=inf,end=inf;
        if(ll<=mid)
            begin=Query_min(2*p,l,mid,ll,rr);
        if(rr>mid)
            end=Query_min(2*p+1,mid+1,r,ll,rr);
        return min(begin,end);
    }
    int Query_max(int p,int l,int r,int ll,int rr)
    {
        if(ll<=l&&r<=rr)
            return Segmax[p];
        int mid=(l+r)/2;
        int begin=-inf,end=-inf;
        if(ll<=mid)
            begin=Query_max(2*p,l,mid,ll,rr);
        if(rr>mid)
            end=Query_max(2*p+1,mid+1,r,ll,rr);
        return max(begin,end);
    }
    
    int main()
    {
        scanf("%d%d",&n,&k);
        Build(1,1,n);
        for(int i=1;i<=n-k+1;i++)
            printf("%d ",Query_min(1,1,n,i,i+k-1));
        printf("
    ");
        for(int i=1;i<=n-k+1;i++)
            printf("%d ",Query_max(1,1,n,i,i+k-1));
        printf("
    ");
    }
    #include<cstdio>
    #include<algorithm>
    #define maxn 1000005
    #define inf 0x3f3f3f3f
    using namespace std;
    int segmin[maxn<<2],segmax[maxn<<2];
    int n,k,value,mi,ma;
    
    void Build(int p,int l,int r)
    {
        if(l==r){
            scanf("%d",&value);
            segmax[p]=segmin[p]=value;
            return;
        }
        int mid=(l+r)/2;
        Build(2*p,l,mid);
        Build(2*p+1,mid+1,r);
        segmin[p]=min(segmin[2*p],segmin[2*p+1]);
        segmax[p]=max(segmax[2*p],segmax[2*p+1]);
    }
    void Query_val(int p,int l,int r,int ll,int rr)
    {
        if(ll<=l&&r<=rr){
            mi=min(mi,segmin[p]);
            ma=max(ma,segmax[p]);
            return;
        }
        int mid=(l+r)/2;
        if(ll<=mid)
            Query_val(2*p,l,mid,ll,rr);
        if(rr>mid)
            Query_val(2*p+1,mid+1,r,ll,rr);
    }
    
    int main()
    {
        scanf("%d%d",&n,&k);
        Build(1,1,n);
        for(int i=1;i<=n-k+1;i++){
            mi=inf,ma=-inf;
            Query_val(1,1,n,i,i+k-1);
            printf("%d ",mi);
        }
        printf("
    ");
        for(int i=1;i<=n-k+1;i++){
            mi=inf,ma=-inf;
            Query_val(1,1,n,i,i+k-1);
            printf("%d ",ma);
        }
        printf("
    ");
    }

    解法二:RMQ

    因为是定长区间,所以数组d[maxn]被优化,只剩下一维.

    #include<cstdio>
    #include<algorithm>
    #define maxn 1000007
    using namespace std;
    int c[maxn],d[maxn];
    int n,k,t=0;
    //d[i]表示从i到i+k-1的一段元素中的最小值 
    void Init1()
    {
        for(int i=1;i<=n;i++)
            d[i]=c[i];
        for(int j=1;j<=t;j++){//j<=t或者(1<<j)<=k 
            for(int i=1;i+(1<<j)-1<=n;i++) 
                d[i]=min(d[i],d[i+(1<<(j-1))]);
        }
    }
    void Init2()
    {
        for(int i=1;i<=n;i++)
            d[i]=c[i];
        for(int j=1;j<=t;j++)
            for(int i=1;i+(1<<j)-1<=n;i++)
                d[i]=max(d[i],d[i+(1<<(j-1))]);
    }
    
    int Query1(int l,int r){
        return min(d[l],d[r-(1<<t)+1]);
    }
    int Query2(int l,int r){
        return max(d[l],d[r-(1<<t)+1]);
    }
    
    int main()
    {//定长区间(长度为k)查找 
        scanf("%d%d",&n,&k);
        while(1<<(t+1)<=k) t++;
        for(int i=1;i<=n;i++)
            scanf("%d",&c[i]);
        Init1();
        for(int i=1;i<=n-k+1;i++){
            if(i!=n-k+1)
                printf("%d ",Query1(i,i+k-1));
            else
                printf("%d
    ",Query1(i,i+k-1));
        } 
        Init2();
        for(int i=1;i<=n-k+1;i++){
            if(i!=n-k+1)
                printf("%d ",Query2(i,i+k-1));
            else
                printf("%d
    ",Query2(i,i+k-1));
        }
    }

    解法三:单调队列

    #include<cstdio> 
    #define maxn 1000005
    using namespace std;
    struct Que
    {
        int value;
        int index;
    }min_que[maxn],max_que[maxn];
     
    int n,k,front,rear,num[maxn];
    int max_rear_inc(int f,int r,int d)
    {//递增队列,队尾插队 
        int mid;
        while(f<=r){
            mid=(f+r)/2;
            if(min_que[mid].value==d)
                return mid;
            else if(min_que[mid].value>d)
                r=mid-1;
            else    
                f=mid+1;
        }
        return f;
    }
    int min_rear_inc(int f,int r,int d)
    {//递减队列,队尾插队 
        int mid;
        while(f<=r){
            mid=(f+r)/2;
            if(max_que[mid].value==d)
                return mid;
            else if(max_que[mid].value>d)
                f=mid+1;
            else
                r=mid-1;
        }
        return f;
    }
    int main()
    {
        //while(scanf("%d%d",&n,&k)!=EOF){
            scanf("%d%d",&n,&k); 
            for(int i=1;i<=n;i++)
                scanf("%d",&num[i]);
            
            min_que[1].value=num[1];
            min_que[1].index=1;
            front=1,rear=1;
            for(int i=2;i<=k;i++){
                rear=max_rear_inc(front,rear,num[i]);
                min_que[rear].value=num[i];
                min_que[rear].index=i;
            }
            printf("%d ",min_que[1].value);
            for(int i=k+1;i<=n;i++){
                rear=max_rear_inc(front,rear,num[i]);
                min_que[rear].value=num[i];
                min_que[rear].index=i;
                if(min_que[front].index<=i-k)
                    front++;
                printf("%d ",min_que[front].value);
            }
            printf("
    ");
            
            max_que[1].value=num[1];
            max_que[1].index=1;
            front=1,rear=1;
            for(int i=2;i<=k;i++){
                rear=min_rear_inc(front,rear,num[i]);
                max_que[rear].value=num[i];
                max_que[rear].index=i;
            }
            printf("%d ",max_que[1].value);
            for(int i=k+1;i<=n;i++){
                rear=min_rear_inc(front,rear,num[i]);
                max_que[rear].value=num[i];
                max_que[rear].index=i;
                if(max_que[front].index<=i-k)
                    front++;
                printf("%d ",max_que[front].value);
            }
            printf("
    ");
        //}
        return 0;
    } 
    #include<cstdio>
    using namespace std;
    #define maxn 2000005
    int n,k;
    int mq_min[maxn],mq_max[maxn],pos[maxn],c[maxn];
    void get_Min()
    {//递增队列 
        int front=1,rear=0;
        for(int i=1;i<k;i++){
            while(front<=rear&&mq_max[rear]>=c[i])
                rear--;
            rear++;
            mq_max[rear]=c[i];//新添加的为较大值 
            pos[rear]=i;
        } 
        for(int i=k;i<=n;i++){
            while(front<=rear&&mq_max[rear]>=c[i])
                rear--;
            rear++;
            mq_max[rear]=c[i];
            pos[rear]=i;
            while(pos[front]<=i-k)
                front++;
            printf("%d ",mq_max[front]);
        }
    }
    void get_Max()
    {//递减队列 
        int front=1,rear=0;
        for(int i=1;i<k;i++){
            while(front<=rear&&mq_min[rear]<=c[i])
                rear--;
            rear++;
            mq_min[rear]=c[i];//新添加的为较小值 
            pos[rear]=i;
        }
        for(int i=k;i<=n;i++){
            while(front<=rear&&mq_min[rear]<=c[i])
                rear--;
            rear++;
            mq_min[rear]=c[i];
            pos[rear]=i;
            while(pos[front]<=i-k)
                front++;
            printf("%d ",mq_min[front]);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
            scanf("%d",&c[i]);
        get_Min();
        printf("
    ");
        get_Max(); 
    }
  • 相关阅读:
    [php代码]从svn获取指定版本,并同步到ftp上。
    java程序用pid重启
    Gearman安装,测试笔记
    ant编译android项目
    jquery代码收藏
    [读书笔记]读《code complete》有感
    无法解析的外部符号_main,该符号在函数_tmainCRTStartup中被引用
    4路电梯调度——pair program总结
    阅读作业2
    必应缤纷桌面的使用测试
  • 原文地址:https://www.cnblogs.com/freinds/p/6422198.html
Copyright © 2011-2022 走看看