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.