poj2823 区间内最值的不同解法

来源:互联网 发布:电脑压力测试软件 编辑:程序博客网 时间:2024/05/28 04:52

题目大意:给定一个n个数的数列,从左至右输出每个长度为m的数列段内的最大/小数。
然后这题到底有什么解法呢?

解法1

这道题的标解肯定是单调队列啦,挺好写,当时好像就是这道题是单调队列的入门来着,时间复杂度O(n)
具体的我就不讲了

#include<cstdio>#include<cstring>const int N=1000005;int n,m;int s[N];int st,ed;int q[N],wei[N];void getmin (){    st=1;ed=0;    for (int u=1;u<m;u++)    {        while (st<=ed&&q[ed]>=s[u]) ed--;        ed++;q[ed]=s[u];wei[ed]=u;    }    for (int u=m;u<=n;u++)    {        while (st<=ed&&q[ed]>=s[u]) ed--;        ed++;q[ed]=s[u];wei[ed]=u;        /*for (int i=st;i<=ed;i++) printf("%d ",q[i]);        printf("\n");*/        while (wei[st]<u-m+1) st++;        printf("%d ",q[st]);    }    printf("\n");}   void getmax (){    st=1;ed=0;    for (int u=1;u<m;u++)    {        while (st<=ed&&q[ed]<=s[u]) ed--;        ed++;q[ed]=s[u];wei[ed]=u;    }    for (int u=m;u<=n;u++)    {        while (st<=ed&&q[ed]<=s[u]) ed--;        ed++;q[ed]=s[u];wei[ed]=u;        while (wei[st]<u-m+1) st++;        printf("%d ",q[st]);    }    printf("\n");}int main(){    scanf("%d%d",&n,&m);    for (int u=1;u<=n;u++)        scanf("%d",&s[u]);    getmin();getmax();    return 0;}

解法2

于是今天由于scy的要求,我又要做一次。。单调队列太无聊了,我们来想一想别的做法
我们可以吧他看做若干个(m-n)个询问,每次就问一个区间的最大(小)值,这就好办了!我们可以对序列进行分块,然后跑,复杂度O(nn)

#include<cstdio>#include<cmath>const int N=200005;const int MAX=1<<30;int n,m;int L[N],R[N],belong[N],cnt;int a[N];int t[N];void prepare ()//预处理分块 {    int nn=sqrt(n);L[1]=1;cnt=1;    for (int u=1;u<=n;u++)    {        belong[u]=cnt;        if (u%nn==0)        {            R[cnt]=u;            cnt++;            L[cnt]=u+1;        }    }    cnt=belong[n];    R[cnt]=n;    for (int u=1;u<=cnt;u++) t[u]=-MAX;    return ;}int mymax (int x,int y) {return x>y?x:y;}int ask (int l,int r){    int lalal=-MAX;    if (belong[l]==belong[r])    {        for (int u=l;u<=r;u++)            lalal=mymax(lalal,a[u]);    }    else    {        for (int u=l;u<=R[belong[l]];u++)   lalal=mymax(lalal,a[u]);        for (int u=belong[l]+1;u<belong[r];u++) lalal=mymax(lalal,t[u]);        for (int u=L[belong[r]];u<=r;u++)   lalal=mymax(lalal,a[u]);    }    return lalal;}int main(){    scanf("%d%d",&n,&m);m--;    prepare();    for (int u=1;u<=n;u++)     {        scanf("%d",&a[u]);          t[belong[u]]=mymax(t[belong[u]],a[u]);    }    for (int u=1;u<=n;u++)    {        if (u+m>n) break;        printf("%d\n",ask(u,u+m));    }    return 0;}

解法三

和解法二差不多,不过我们的最值问题可以用线段树来维护一下,然后时间复杂度是O(nlogn)

解法四

假如我们看做势若干个询问的话,我们其实可以考虑莫队。。其实已经排好序的啦,并不需要什么操作,好想复杂度都是从n根号n变为线性了。。然后怎么维护呢,我们可以对数离散化,然后维护权值线段树就好了。。到时候询问就跑就好了,时间复杂度是O(nlogn)

解法五

哦,对了,做了解法二都忘了RMQ了。。思路和分块是一样的,但是时间复杂度是O(nlogn),不过空间会大一些

解法六

poj的讨论上面发现的

这题用C++ STL的priority_queue同时维护一个最大堆和一个最小堆就过了

我想了一下,也可以,就维护一个堆嘛。。然后你每次超出范围的数就删掉。。怎么删呢,可以打标记,到时候再跳过。我对堆也不是很熟,但当时研究左偏树的时候知道这玩意是可以logn删除指定元素的,无聊的人也可以打一打。。但无论哪一个都是O(nlogn)

解法七

我暂时只想到这么多啦!要是还有就是暴力加玄学优化啦!

原创粉丝点击