題目傳送門
輸入乙個長度為 n
nn 的整數序列,從中找出一段長度不超過 m
mm 的連續子串行,使得子串行中所有數的和最大。
注意:子串行的長度至少是 111。
第一行輸入兩個整數 n
nn ,mmm。
第二行輸入 n
nn 個數,代表長度為 n
nn 的整數序列。
同一行數之間用空格隔開。
輸出乙個整數,代表該序列的最大子序和。
1 11≤
\leq≤nn
n ,mmm≤
\leq
≤300000
300000
300000
6 41 -3 5 1 -2 3
演算法1:暴力列舉 o(n
m)
o(nm)
o(nm)
通過兩層迴圈列舉每乙個不超過m
mm的連續子串行
code
#include
using
namespace std;
const
int n=
3e5+10;
int n,m,a[n]
,maxn=-1
,sum=0;
intmain()
} cout
}
演算法2:字首和優化 o(n
m)
o(nm)
o(nm)
利用字首和解決演算法1中的sum
sumsu
m的累加過程
#include
using
namespace std;
const
int n=
3e5+10;
int n,m,a[n]
,s[n]
,maxn=-1
,sum=0;
intmain()
cout
}
演算法3:優先佇列優化 o(n
)o(n)
o(n)
假設ss
s陣列中的三個下標分別為 i,j
,k(k
<
j
i,j,k(ki,
j,k(
k<
j根據演算法2中的字首和優化,在 i
ii保持不變的情況下,s[i
]s[i]
s[i]
也保持不變,序列的中所有數的累加和為s[i
]−s[
left
]s[i]-s[left]
s[i]−s
[lef
t],所以s[l
eft]
s[left]
s[left
]越小,序列中的數字和就越大,也就更優。
那麼如果s[k
]≥s[
j]
s[k] \geq s[j]
s[k]≥s
[j]那k
kk一定是乙個無用的點,因為相比較而言,k
kk的位置以及s
ss陣列中的值都不如j
jj號點,所以k
kk就沒有任何價值
所以可以設計乙個演算法
用乙個佇列來模擬以下的操作,佇列儲存的是這個子序中每個元素的編號
從前向後遍歷,如果i
ii到隊首的元素編號q[h
ead]
q[head]
q[head
]超過了m
mm,就把隊首往後推
如果我當前這個元素的值要比隊尾的元素更優,即s[i
]
q[ta
il]]
s[i]s[
i]q[ta
il]]
(從前面的講解可知,元素的價值取決於它的位置和s
ss陣列中的值,因為是從前向後遍歷,所以當前位置一定比佇列中任何乙個數的位置更優,所以只需要比較s
ss陣列中的值)就將隊尾的元素刪去,並把這個元素塞到隊尾
由此可知,當前維護的佇列一定是乙個具有單調性的(從隊首到隊尾的元素均從大到小或從小到大),因此稱為單調佇列
最後不斷更新ans的值就可以啦
由於每個點都只會進出佇列一次,所以時間複雜度就是o(n
)o(n)
o(n)
#include
using
namespace std;
const
int n=
300010
;int a[n]
,s[n]
,q[n]
,head=
0,tail=0;
intmain()
for(
int i=
1;i<=n;i++
)printf
("%d"
,ans)
;return0;
}
演算法競賽高階指南 0x00
快速冪模板,寫一下快速冪的原理。我們知道,乙個數 n 在二進位制 也可以是其他進製 下可以被表示為 a 1 a 2 2 1 a 3 2 2 a m 2 那麼我們可以考慮將其分拆成二進位制狀態下的每一位,然後做冪運算。這樣做的時間複雜度為 o log 2 n 實現的過程類似於倒過來的分治 當然也可以直...
演算法競賽高階指南 0x12 最大子序和
這其實是一道單調佇列優化區間dp問題,對於這個序列,我們可以劃分為n個區間,每個區間代表以ai結尾,長度不超過m的子串行和,我們只需要遍歷一遍每個集合,找到每乙個集合中的最大值,那麼就可以更新出這個序列的最大值 如何找每個集合中的最大值呢,最大值就是要求出以a k 結尾,長度不超過m的子串行和最大,...
演算法競賽高階指南 0x12 佇列 蚯蚓
m次操作,每次都要將乙個最大的切成兩段,然後再加上乙個偏移量,然後將兩段全部放入佇列中,但是這樣是o mlogm 看題中資料範圍肯定會超時,那麼我就要繼續優化 我們可以發現,先將原序列從大到小排列 q1,q2,q3,q4 第一次肯定是切割q1,假設將q1切成了q1l,q1r,那麼對於第二次切割只需要...