單調佇列 學習筆記

2021-08-20 23:21:31 字數 4378 閱讀 7707

q:

給定乙個長度為n的序列(可能有負數),

從中找出一段長度不超過m的連續子串行,使得其和最大

n

≤500000

n≤500000

n≤5000

00 a:

對於這題

首先不難想到先求出數列字首和sum

那麼顯然問題的答案就是max

i=mn

(sum

[i]−

minj

=i−m

i−1(

sum[

j]))

max_^n(\ sum[i]-min_^(sum[j])\ )

maxi=m

n​(s

um[i

]−mi

nj=i

−mi−

1​(s

um[j

]))

通俗的說

我們需要維護整個數列中所有長度為m的連續子串行的最小值

線段樹??st表???

雖然都是nlogn的演算法,但對於這題顯然還是略顯吃力

所以這時考慮引入我們的單調佇列

單調佇列需要乙個雙端佇列實現

可以直接維護每個長度為m的連續子數列的最小值

具體怎麼做呢

我們從第乙個元素一次往後遍歷整個序列,每遍歷乙個元素

1.檢查當前佇列中隊首元素位置距當前元素是否超過m,是則不斷從隊首彈出直到小於m個

2.檢查隊尾元素是否大於等於當前元素,是則不斷從隊尾彈出元素,

直到隊列為空隊尾元素小於當前元素

3.將當前元素入隊

此時隊首儲存的就是從當前元素開始往前m個元素中的最小值了

整個演算法複雜度只有o(n

)o(n)

o(n)

當然,我們在實際操作時

佇列中可以只儲存編號,進行2操作時只要對映到原序列對應元素

這樣就可以省去用結構體同時儲存值與編號的麻煩

洛谷p1714 切蛋糕

就是上面的問題

#include

#include

#include

#include

#include

#include

using

namespace std;

intread()

while

(ss>=

'0'&&ss<=

'9')

return f*x;

}const

int maxn=

500010

;int n,m;

int sum[maxn]

;int q[maxn]

,ll=

1,rr=1;

int ans=

-1e9

;int

main()

printf

("%d"

,ans)

;return0;

}

洛谷p1725 琪露諾

有n+1個格仔,起點在格仔0,每個格仔有乙個數值,每次能向編號大的格仔跳,跳躍距離為[l,r],求能得到的最大分值總和

d p[

i]

dp[i]

dp[i

]表示跳到i

ii能獲得的最大分數,dp[

i]=d

p[j]

+a[i

](i−

r≤j≤

i−l)

dp[i]=dp[j]+a[i](i-r\leq j\leq i-l)

dp[i]=

dp[j

]+a[

i](i

−r≤j

≤i−l

)單調佇列維護的區間與當前位置不連續

ll=rr=

1; dp[0]

=a[0];

for(

int i=l;i<=n;

++i)

拓展

在上述問題的基礎上,若給定的格仔位置不連續(pos[i]為第i個格仔的位置)

顯然每次能轉移到pos[i]的合法區間會隨著i的增加而增加(即轉移區間有單調性)

可以用乙個指標p維護距離當前位置大於等於l的最近的位置,即先讓隊內元素滿足j≤i

−l

j\leq i-l

j≤i−

l再不斷彈出隊首不滿足i−r

≤j

i-r\leq j

i−r≤

j的位置

語死早,不太會描述,看看**意會一下吧

int p=

0; ll=rr=1;

memset

(dp,

128,

sizeof

(dp)

); dp[0]

=a[0];

for(

int i=

1;i<=n;

++i)

while

(pos[i]

-pos[q[ll]

]>r&&ll++ll;

if(ll>=rr||dp[q[ll]]==

-inf)

continue

;//沒有合法的位置可以轉移

dp[i]

=dp[q[ll]

]+a[i];if

(i>=n-r) ans=

max(ans,dp[i]);

}

洛谷p3512 [poi2010]pil-pilots

給定n,k和乙個長度為n的序列,求最長的最大值最小值相差不超過k的序列

二分序列長度mid,單調佇列找每個長度為mid的子串行的最小/大值即可

#include

#include

#include

#include

#include

#include

using

namespace std;

intread()

while

(ss>=

'0'&&ss<=

'9')

return f*x;

}const

int maxn=

3000010

;int n,k;

int a[maxn]

;int q[2]

[maxn]

,ll[2]

,rr[2]

;int ans;

intcheck

(int m)

return0;

}int

main()

if(check

(l))ans=l;

printf

("%d"

,ans)

;return0;

}

一樣二分花盆寬度,單調佇列檢查該長度區間內的y座標最大/小值差值

#include

#include

#include

#include

#include

#include

using

namespace std;

typedef

long

long lt;

intread()

while

(ss>=

'0'&&ss<=

'9')

return f*x;

}const

int maxn=

100010

;int n,d,mx;

struct nodep[maxn]

;int q[2]

[maxn]

,ll[2]

,rr[2]

;bool

cmp(node a,node b)

intcheck

(int w)

return0;

}int

main()

if(ans==0)

printf

("-1");

else

printf

("%d"

,ans)

;return0;

}

單調佇列學習筆記

單調佇列學習筆記 by menci 輔助佇列 m 即為單調佇列 luogu p3957 跳房子 noip2017普及組 跳房子 顯然答案有單調性,所以二分答案。判斷時 dp。f i 表示跳前i個格仔,且停在第 i 個格仔最大分數 sc ore i 表示第 i 個格仔的分數。易得轉移方程 f i m ...

單調佇列 學習筆記

單調佇列是一種特殊的雙端佇列,其滿足單調性,即內部元素單調遞增或單調遞減。單調佇列可以用陣列模擬,也可以用 stl 中的 deque 實現。例題 最大子序和 給定乙個長度為 n 的整數序列,從中找出一段長度不超過 m 的連續子串行,使得子串行中所有數的和最大。n,m leq 3 10 5 區間和可以...

單調佇列和單調棧學習筆記

單調棧 單調棧是指乙個棧內部的元素是具有嚴格單調性的一種資料結構,分為單調遞增棧和單調遞減棧。單調棧有兩個性質 1.滿足從棧頂到棧底的元素具有嚴格的單調性 2.滿足棧的後進先出特性越靠近棧底的元素越早進棧 元素進棧過程 對於乙個單調遞增棧來說 若當前進棧的元素為 a 如果a 棧頂元素則直接將a 進棧...