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 進棧...