今天寫了人生中第乙個單調佇列,激動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加入佇列,...