RMQ——窗口题解
题目:窗口
描述:
【问题描述】
给你一个长度为N的数组,一个长为K的滑动的窗体从最左移至最右端,你只能见到窗口的K个数,每次窗体向右移动一位,如下表:
Window position | Min value | Max value |
[1 3 -1] -3 5 3 6 7 | -1 | 3 |
1 [3 -1 -3] 5 3 6 7 | -3 | 3 |
1 3 [-1 -3 5]3 6 7 | -3 | 5 |
1 3 -1 [-3 5 3] 6 7 | -3 | 5 |
1 3 -1 -3 [5 3 6] 7 | 3 | 6 |
1 3 -1 -3 5 [3 6 7 ] | 3 | 7 |
你的任务是找出窗口在各位置时的max value,min value.
输入格式:
第一行n,k,第二行为长度为n的数组
输出格式:
第一行每个位置的min value,第二行每个位置的max value
样例
:
window.in
8 3
1 3 -1 -3 5 3 6 7
window.out
-1 -3 -3 -3 3 3
3 3 5 5 6 7
数据范围:
20%:n≤500; 50%:n≤100000;
100%:n≤1000000;
这道题有一点难度,首先数据规模比较大,再有对于内存来说也要稍加注意,具体写法如下分析:
算法一:大暴力
预计得分:20
分析:
暴力……貌似不用解释……直接两重循环查找,但是时间复杂度最坏为$O(N2/2)$左右,从数据上来看,20分稳拿,但是数据一大,就不好说了……RP极好时30分,没什么分析的意义。
算法二:RMQ
预计得分:100
分析:
第一步:内存压缩
100 0000级的数据处理,对RMQ来说,也是略有压力(总比暴力强)……而且需要计算一下内存。两个数组存储,log2100 0000约为20,即开两个20*100 0000的数组,再加上之前100 0000的数据,总共内存为312MB左右,爆M了……所以,必须优化!!!首先,两个数组完全可以开成一个,先剩一半内存,然后再将存初始数据的数组去掉(但我提交时没有去,反正不碍事),最后内存变为了152MB,还可以。
第二步:时间压缩
观察题目上的输出要求可以发现,先输出所有序列最小值,再输出所有序列的最大值,这样的话就可以用一个数组解决,省下的大把内存不说,还节约了时间,每算好一个直接输出即可,或者拿ansistring先存着?不知行不行,回头试试。如果是交替输出的话(完全可以这么改)……自己回去想去,内存反正超不了。
言归正传,之所以用RMQ算法,就是看中了其$O(1)$的查找速度,多么省时间!经过估计,按照之前的思想,两次处理与输出,时间复杂度最坏为$O(2*NlogN+N)$,即两次处理2*NlogN,最坏区间长度为N/2(想一想,为什么),则两次操作总复杂度为N。共为$O(2*NlogN+N)$,但是,经计算得知,会超时啊!!!怎么办?仔细看看题目所说,原来是2s的时限,吓得我被装进了背包……
反正最后A掉了这道题,一遍过无压力。总时间跑地有点慢,具体如下图(别问我背景图片是什么,我不知道……):
后来看大神的评论说,用什么单调队列,STL,线段树(有人说会被卡)等算法A掉,总之时间最快为0.382s,根本没法比啊……
AC代码:
{
program zht; var n,m,j,i,k,x,ans:longint; a:array[0..1000000] of longint; f:array[0..1000000,0..20] of longint; function min(a,b:longint):longint; begin if a<=b then min:=a else min:=b; end; function max(a,b:longint):longint; begin if a>=b then max:=a else max:=b; end; begin assign(input,'window.in'); assign(output,'window.out'); reset(input); rewrite(output); readln(n,k); fillchar(f,sizeof(f),$7f); for i:=1 to n do begin read(a[i]); f[i,0]:=a[i]; end; for j:=1 to trunc(ln(n)/ln(2)) do for i:=1 to n+1-(1 shl j) do f[i,j]:=min(f[i,j-1],f[i+1 shl (j-1),j-1]); // 初始化 x:=trunc(ln(k)/ln(2)); for i:=1 to n-k+1 do begin ans:=min(f[i,x],f[i+k-(1 shl (x)),x]); write(ans,' '); end; writeln; // 处理最小值 fillchar(f,sizeof(f),0); for i:=1 to n do f[i,0]:=a[i]; for j:=1 to trunc(ln(n)/ln(2)) do for i:=1 to n+1-(1 shl j) do f[i,j]:=max(f[i,j-1],f[i+1 shl (j-1),j-1]); // 还是初始化 for i:=1 to n-k+1 do begin ans:=max(f[i,x],f[i+k-(1 shl (x)),x]); write(ans,' '); end; writeln; // 处理最大值 close(input); close(output); end.
}