单调队列

滑动窗口
现在有一堆数字共N个数字(N<=10^6),以及一个大小为k的窗口。现在这个从左边开始向右滑动,每次滑动一个单位,求出每次滑动后窗口中的最大值和最小值。例如:The array is [1 3 -1 -3 5 3 6 7], and k = 3.
     窗口                    最小值        最大值
[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 -35 [3 6 7]           3             7
输入格式
输入一共有两行,第一行为n,k。第二行为n个数(<INT_MAX).
输出格式
输出共两行,第一行为每次窗口滑动的最小值第二行为每次窗口滑动的最大值
样例输入
8 31 3 -1 -3 5 3 6 7
样例输出
-1 -3 -3 -3 3 33 3 5 5 6 7
注释

50%的数据,n<=10^5100%的数据,n<=10^6

这是典型的给定队列长度,在O(n)内求区间[l,r]中的最大或最小值问题。

设队列b[],用来存值的下标(方便判断队列中的数是否在区间内)

现在求最大值

1.元素入队前,如果该数大于队列中它前面的数(j>i,a[j]>a[i])那么前面的数不会是最大值,也没有意义,因此删去。(同时该操作保证了队列中元素的单调性)

2.介于单调性,用二分优化:在队列b[head]~b[tail]中找到最后一个a[i]<a[b[k]],那么i值直接插到k+1处,即tail=k+1

3.判断当前队头在不在所求区间中,若不在,出队

   while b[head]<i-k+1 do head:=head+1;

4.当前区间的最大值即为队头a[b[head]]

最小值类似

var n,i,k,head,tail,j,k1:longint;
    a,b:array[1..1000000]of longint;
    f:array[1..1000000]of longint;
<span style="color:#3333ff;">function check(l,r,x:longint):longint;//二分
var mid:longint;
begin
 repeat
  mid:=(l+r) div 2;
  if x>a[b[mid]] then
   l:=mid+1
  else
   r:=mid-1;
 until l>r;
 check:=l;
end;</span>
function check1(l,r,x:longint):longint;
var mid:longint;
begin
 repeat
  mid:=(l+r) div 2;
  if x>a[b[mid]] then
   r:=mid-1
  else
   l:=mid+1;
 until l>r;
 check1:=l;
end;
begin
 read(n,k);
 for i:=1 to n do read(a[i]);
 f[1]:=a[1];
 head:=1;tail:=1;
 b[1]:=1;
 <span style="color:#3333ff;">for i:=2 to n do <span style="font-family: Arial, Helvetica, sans-serif;">//最小值</span>
  begin
   k1:=check(head,tail,a[i]);
   tail:=k1;
   b[tail]:=i;
   while b[head]<i-k+1 do head:=head+1;
   f[i]:=a[b[head]];
  end;</span>
 for i:=k to n do if i<n then write(f[i],' ') else writeln(f[i]);
 head:=1;tail:=1;
 b[1]:=1;
 for i:=2 to n do //最大值
  begin
   k1:=check1(head,tail,a[i]);
   tail:=k1;
   b[tail]:=i;
   while b[head]<i-k+1 do head:=head+1;
   f[i]:=a[b[head]];
  end;
 for i:=k to n do if i<n then write(f[i],' ') else writeln(f[i]);
end.


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值