【单调队列】Window+(window.pas/c/cpp;时限+2s;256M)

 Window
(window.pas/c/cpp;时限 2s;256M) 


 
给你一个长度为 N 的数组,一个长为 K 的滑动的窗体从最左移至最右端,
你只能见到窗口的K个数,每次窗体向右移动一位,如下表:
 
Window position  Min value  Max value 

 
你的任务是找出窗口在各位置时的 max value,min value.
 
输入格式:
第 1 行 n,k,第 2 行为长度为n的数组
 
输出格式:
2 行,第 1 行每个位置的 min value,第 2 行每个位置的 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;

==================================

单调队列

--------------

记得去年做的时候我调了一下午...

今天调了一晚上...

但当我写完看别人才写50多行的时候...

我哭了...

=========================

var
  n,k:longint;
  max_dui,min_dui,sit_min,sit_max:array[1..1000000]of longint;
  max_l,max_r,min_l,min_r:longint;
  max:array[1..1000000]of longint;
  max_t:longint;
procedure init;
begin
  assign(input,'window.in');
  assign(output,'window.out');
  reset(input); rewrite(output);
end;

procedure terminate;
begin
  close(input); close(output);
  halt;
end;

procedure main;
var
  i:longint;
  x:longint;
begin
  readln(n,k);
  max_l:=1; max_r:=0;
  min_l:=1; min_r:=0;
  max_t:=0;
  for i:=1 to k do   //初始化
    begin
      read(x);
      if (min_l>min_r)or(x>min_dui[min_r])  then
        //对于最小值队列若大于尾指针则插入..
        //或者左指针大于右指针则插入
        begin
          inc(min_r);
          min_dui[min_r]:=x;
          sit_min[min_r]:=i;
        end
        else
        //若小于尾指针则弹出全部大于x的元素
        //插入新元素到队尾
        begin
          while (min_r>=min_l)and(min_dui[min_r]>=x) do dec(min_r);
          inc(min_r);
          min_dui[min_r]:=x;
          sit_min[min_r]:=i;
        end;
      if (max_l>max_r)or(x<max_dui[max_r]) then
        //对于最大值队列若小于尾指针则插入..
        //或者左指针大于右指针则插入
        begin
          inc(max_r);
          max_dui[max_r]:=x;
          sit_max[max_r]:=i;
        end
        else
        //若大于队尾指针则弹出全部队首元素
        //插入新元素到队尾
        begin
          while (max_r>=max_l)and(max_dui[max_r]<=x) do dec(max_r);
          inc(max_r);
          max_dui[max_r]:=x;
          sit_max[max_r]:=i;
        end;
    end;
  write(min_dui[min_l],' ');
  inc(max_t);
  max[max_t]:=max_dui[max_l];
  //if min_r-min_l>k then inc(min_l);
  //if max_r-max_l>k then inc(max_l);
  for i:=k+1 to n do
    //从k+1位开始处理
    begin
      read(x);
      {维护最小值队列}
      if (min_l>min_r)or(x>min_dui[min_r])  then
        begin
          inc(min_r);
          min_dui[min_r]:=x;
          sit_min[min_r]:=i;
          while (i-sit_min[min_l]+1>k) do inc(min_l); //保证队列长度为k
          write(min_dui[min_l],' ');
        end
        else
        begin
          while (min_r>=min_l)and(min_dui[min_r]>=x) do dec(min_r);//更新单调队列
          inc(min_r);        //增加尾指针
          min_dui[min_r]:=x;
          sit_min[min_r]:=i;
          while (i-sit_min[min_l]+1>k) do inc(min_l); //保证队列长度为k
          write(min_dui[min_l],' ');
        end;
      {维护最大值队列}
      if (max_l>max_r)or(x<max_dui[max_r]) then
        begin
          inc(max_r);
          max_dui[max_r]:=x;
          sit_max[max_r]:=i;
          while (i-sit_max[max_l]+1>k) do inc(max_l);
          inc(max_t);
          max[max_t]:=max_dui[max_l];
        end
        else
        begin                                 {之前有错}
          while (max_r>=max_l)and(max_dui[max_r]<=x) do dec(max_r);//更新单调队列
          inc(max_r);
          max_dui[max_r]:=x;
          sit_max[max_r]:=i;
          while (i-sit_max[max_l]+1>k) do inc(max_l);
          inc(max_t);
          max[max_t]:=max_dui[max_l];
        end;
    end;
  writeln;
  for i:=1 to max_t do
    begin
      write(max[i],' ');
    end;
  writeln;
end;

begin
  init;
  main;
  terminate;
end.

 -----------------------------------

精简后的代码

--------------------

type
  re=record
       s,v:longint;
     end;
var
  n,k:longint;
  a:array[1..1000000]of longint;
  dui:array[1..1000000]of re;
  s,t:longint;
procedure init;
begin
  assign(input,'window.in');
  assign(output,'window.out');
  reset(input); rewrite(output);
end;

procedure terminate;
begin
  close(input); close(output);
  halt;
end;

procedure min;
var
  i:longint;
begin
  s:=1; t:=0;
  for i:=1 to n do
    begin
      while (s<=t)and(dui[t].v>=a[i]) do dec(t);
      inc(t);
      dui[t].v:=a[i];
      dui[t].s:=i;
      while dui[t].s-dui[s].s>=k do inc(s);
      if i>=k then write(dui[s].v,' ');
    end;
end;

procedure max;
var
  i:longint;
begin
  s:=1; t:=0;
  for i:=1 to n do
    begin
      while (s<=t)and(dui[t].v<=a[i]) do dec(t);
      inc(t);
      dui[t].v:=a[i];
      dui[t].s:=i;
      while dui[t].s-dui[s].s>=k do inc(s);
      if i>=k then write(dui[s].v,' ');
    end;
end;

procedure main;
var
  i:longint;
  x:longint;
begin
  readln(n,k);
  for i:=1 to n do read(a[i]);
  min;
  writeln;
  max;
end;

begin
  init;
  main;
  terminate;
end.


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值