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.
}

转载于:https://www.cnblogs.com/zhtjtcz/p/4994436.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值