單調佇列 入門

2021-06-08 08:37:46 字數 2940 閱讀 9002

今天寫了人生中第乙個單調佇列,激動ing~

先看一道單調佇列的入門題:

乙個含有n項的數列(n

<=2000000),求出每一項前面的第m個數到它這個區間內的最小值。

先寫出動規方程:f[i]=min(j合法)

很明顯的,這是乙個n^2的動規,但是,我們可以注意到,數列中有些數無論如何都不會被選到.

如:-1 -2 8 -2 0  -4 -9 -1 中的8就不能被選,且對f[i](i>1),第乙個-1也沒有用.

一般的,對a[i],a[j],若有a[j]>a[i],jj且a[i]於是,我們就可以將冗餘的元素刪掉,以提高效率.

假設我們已經將a[i]以前的冗餘資料刪掉,剩下的元素如果按原來的順序(我們稱它為pos值)排成一排,那麼我們將會看到乙個序列,我們設這個序列為b,顯然的,b序列的key值是單調遞增的,我們的任務就是動態的維護這個序列.

乙個顯而易見的結論是,如果a[i]比該序列中的b[j]優,那麼a[i]一定比該序列的最後乙個元素優,即,將a[i]新增進這個序列後,刪去的元素一定是序列後面的一段連續的元素.因此,我們得到了乙個將a[i]加入b序列的演算法:先將a[i]加入b的最後面,然後一直刪去a[i]前乙個比a[i]劣的元素知道只剩乙個元素或不能再刪.

由於只需要求出每個元素的前m個元素中的min,所以b序列中過於靠前的一些元素也是沒有意義的,我們要將他們刪去.由於b是按在a中的位置排序的,所以刪除的過程也是將前面的一段連續的元素刪去.

這樣,我們就得到了乙個動態維護b序列的方法,序列b就是所謂的單調佇列.

由於只有b序列中的元素才是有可能影響接下來的f值的,所以我們在計算f時,只要在b中找到乙個最小的就行了.而b序列的key值是單調的!!!

這樣就直接導致了我們的演算法:每次將a[i]加入b,把該刪掉刪掉,取出隊首元素作為f[i]的值.

由於每個元素最多進隊出隊各一次,所以整個演算法的複雜度是線性的,完全可以滿足題目的要求.

單調佇列是這樣的乙個佇列:佇列中每個元素有兩個域:key和pos,我們隨時保持佇列中元素的key值單調(遞增或遞減),pos值遞增,每次新增元素時將隊尾元素不斷刪除,然後將該元素加入隊尾.在適當的時候還要將隊首元素也刪掉.利用這樣一種雙端佇列,我們可以進行一些優化.

推薦幾個入門題:poj2823,完全是裸的單調佇列.

hdoj 3415,要轉一下模型,不過也蠻裸.

下面是我的**(略醜,畢竟剛剛寫):

program poj2823;

type

int=longint;

var i,j,k,m,n:int;

num,pos,a:array[1..1000000]of int;

p,q:int;

begin

read(n,k);

for i:=1 to n do read(a[i]);

p:=1;q:=0;

for i:=1 to n do begin

while(q<>p-1)and(a[i]=k then write(num[p],' ');

end;

writeln;

p:=1;q:=0;

for i:=1 to n do begin

while(q<>p-1)and(a[i]>num[q])do dec(q);

inc(q);num[q]:=a[i];pos[q]:=i;

if(pos[p]+k=i)then inc(p);

if i>=k then write(num[p],' ');

end;

end.

hdoj3415

program hdoj3415;

type

int=longint;

var i,j,k,m,n,length:int;

a,num,pos:array[1..200000]of int;

s:array[0..200000]of int;

l,r,p,q,max:int;

ii,t:int;

function ff(i:int):int;

begin

while i>n do i:=i-n;

ff:=i;

end;

procedure main;

begin

read(n,k);

if k>n then k:=n;

for i:=1 to n do begin

read(a[i]);a[i+n]:=a[i];

end;

s[0]:=0;

for i:=1 to n*2 do s[i]:=s[i-1]+a[i];

max:=-maxlongint;

p:=1;q:=0;

for i:=1 to n*2 do begin

while(q<>p-1)and(s[i-1]k)then inc(p);

if (max<=s[i]-num[p]) then begin

if(max=s[i]-num[p])then begin

if(ff(pos[p])>l)then continue;

if(ff(pos[p])=l)and(i-pos[p]>length)then continue;

l:=ff(pos[p]);r:=ff(i);

end else begin

max:=s[i]-num[p];

l:=ff(pos[p]);r:=ff(i);

length:=i-pos[p];

end;

end;

end;

writeln(max,' ',l,' ',r);

end;

begin

read(t);

for ii:=1 to t do main;

end.

by qw

單調佇列入門

單調佇列是一種佇列 廢話 其中佇列的元素保證是單調遞增或者是單調遞減的 那麼隊首的元素不就是最小 或最大 的嗎?我們結合具體的題目來看看吧 傳送門 p1886 滑動視窗 現在有一堆數字共n個數字 n 10 6 以及乙個大小為k 的視窗。現在這個從左邊開始向右滑動,每次滑動乙個單 位,求出每次滑動後視...

單調佇列入門

單調佇列這個名字就指明了它的性質 單調性,是乙個單調的雙端佇列 下面列出deque的常用成員函式 來自 單調佇列入門題目 p1886 滑動視窗 模板 單調佇列 這裡我們只討論最大值,最小值原理一樣的 很明顯,當我們在計算區間 i k 1,i 的最大值時,是不是區間 i k 1,i 1 我們之前已經計...

單調佇列入門

給定長度為n的數列a和整數k,求b i min,複雜度為o n 最開始單調隊列為空,保證佇列中的元素始終保持單調性 為了計算b 0 把0到k 1依次加入佇列。在加入i時,當單調佇列的末尾的值j滿足a j a i 則不斷取出。直到雙端隊列為空或者a j a i 之後再於末尾加入i。等到k 1加入佇列,...