题目vj地址:POJ 2823
题意:给出一个序列,长度为n,n最大1e6 。k表示区间长度。问区间长度为k的区间,最大值和最小值分别是多少。
分析: 区间最大值和区间最小值,很容易想到线段树和树状数组等等。由于n最大1e6 线段树查询一次的复杂度logn 所以时间上是没问题了。
先上个线段树AC代码 。。。。 后面还有deque的做法分析。
时间:10313ms ,内存 44712kB
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <string>
#include <map>
#include <stack>
#include <queue>
#define INF 0x3f3f3f3f
using namespace std;
const int N=1e6+100;
typedef long long ll;
int n,k;
int a[N];
int x[N];
int y[N];
struct Node
{
int l,r;
int maxx;
int minn;
}T[N<<2];
void pushup(int p)
{
T[p].maxx=max(T[p<<1].maxx,T[p<<1|1].maxx);
T[p].minn=min(T[p<<1].minn,T[p<<1|1].minn);
}
void build(int l,int r,int p)
{
T[p].l=l,T[p].r=r;
if(l==r)
{
T[p].maxx=a[l];
T[p].minn=a[l];
return ;
}
int mid=(l+r)>>1;
build(l,mid,p<<1),build(mid+1,r,p<<1|1);
pushup(p);
}
int query_max(int l,int r,int p)
{
if(l<=T[p].l&&T[p].r<=r)
{
return T[p].maxx;
}
int mid=(T[p].l+T[p].r)>>1;
int ans=-INF;
if(l<=mid)ans=max(ans,query_max(l,r,p<<1));
if(r>mid)ans=max(ans,query_max(l,r,p<<1|1));
return ans;
}
int query_min(int l,int r,int p)
{
if(l<=T[p].l&&T[p].r<=r)
{
return T[p].minn;
}
int mid=(T[p].l+T[p].r)>>1;
int ans=INF;
if(l<=mid)ans=min(ans,query_min(l,r,p<<1));
if(r>mid)ans=min(ans,query_min(l,r,p<<1|1));
return ans;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
}
build(1,n,1);
for(int i=1;i<=n;i++)
{
x[i]=query_max(i,i+k-1,1);
}
for(int i=1;i<=n;i++)
{
y[i]=query_min(i,i+k-1,1);
}
for(int i=1;i<=n-k+1;i++)
{
printf("%d%c",y[i],i==n-k+1?'\n':' ');
}
for(int i=1;i<=n-k+1;i++)
{
printf("%d%c",x[i],i==n-k+1?'\n':' ');
}
return 0;
}
为什么再说下deque的做法呢,因为空间,线段树的空间很大,如果出题者特意要卡内存的话,那么线段树就JJ了。
这里介绍deque的思想。
首先我们要知道,每次算区间的时候,整个区间k相当于移动了一格。那么我们没必要重新去计算。 我们每次去计算的时候,先判断下,队列顶的元素是不是超出我们的区间范围了,如果超出了,那么pop掉,因为超出区间范围,我们没必要算他了。如果当前元素大于队列顶部的那个元素,我们需要把队列顶部的那个元素弹出,因为后面最大值将不会是他了。判断好上面的情况后,我们需要把这个元素加到队列尾部,但是加的时候,我们需要去判断当前元素是不是大于尾部元素,如果大于尾部元素,也pop掉。 这是最大值的做法,最小值也雷同,只要改变符号即可
AC代码:
时间:9266ms ,内存:15768kB
deque的复杂度是O(n)
时间和内存都优于线段树
#include <iostream>
#include <algorithm>
#include <deque>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=1e6+100;
int n,k;
struct Node
{
int val;
int pos;
}a[N];
deque<Node>dq;//计算最大值
deque<Node>dq2;//计算最小值
int x[N];
int y[N];
void solve()
{
//处理最大值
for(int i=1;i<=n;i++)
{
if(dq.empty())
{//如果是空的,直接加入
dq.push_back(a[i]);
}
else
{
Node top=dq.front();//访问头结点
if(i-top.pos>=k)
{
dq.pop_front();
}//如果超出区间直接删掉
if(a[i].val>dq.front().val)
{
dq.pop_front();
}
while(!dq.empty()&&a[i].val>dq.back().val)
{
dq.pop_back();
}
dq.push_back(a[i]);
}
x[i]=dq.front().val;
}
//处理最小值
for(int i=1;i<=n;i++)
{
if(dq2.empty())
{//如果是空的,直接加入
dq2.push_back(a[i]);
}
else
{
Node top=dq2.front();//访问头结点
if(i-top.pos>=k)
{
dq2.pop_front();
}//如果超出区间直接删掉
if(a[i].val<dq2.front().val)
{
dq2.pop_front();
}
while(!dq2.empty()&&a[i].val<dq2.back().val)
{
dq2.pop_back();
}
dq2.push_back(a[i]);
}
y[i]=dq2.front().val;
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].val);
a[i].pos=i;
}
solve();
for(int i=k;i<=n;i++)
{
printf("%d%c",y[i],i==n?'\n':' ');
}
for(int i=k;i<=n;i++)
{
printf("%d%c",x[i],i==n?'\n':' ');
}
return 0;
}