1. 程式人生 > >最大子序和(單調列隊)

最大子序和(單調列隊)

ACM題集:https://blog.csdn.net/weixin_39778570/article/details/83187443
最大子序和
題目:http://contest-hunter.org:83/contest/0x10「基本資料結構」例題/1201 最大子序和
題意:輸入一個長度為n的整數序列,從中找出一段不超過m的連續子序列,使得整個序列的和最大。

解法:

單調列隊,表示下標上升,對應的字首和也遞增的列隊。
列隊中儲存的是字首和的下標。
由於求的是連續不超過m的最大自序和。所以列隊的左端(隊頭),儲存的下標不能小於i-m,i表示當前遍歷的字首和的下標。
對於本問題,找到一個左端點 j,其中j屬於[i-m,i-1]並且S[j]最小。
不妨比較一下任意兩個位置j,k,如果k<j<i並且S[k]>=S[j],那麼對於所有大於等於i的右端點,k永遠不會成為最優選擇。因為S[k]不小於S[j],而且j離得比i遠,長度更不容易超過M,即j的生存能力比k更強。所以當這樣一個j出現了的時候,k就完全是一個無用的位置了。

#include<bits/stdc++.h>
#define ll long long
#define fo(i,j,n) for(register int i=j; i<=n; ++i)
using namespace std;
int q[300005],sum[300005],a[300005];
int n,m;
void solve(){
	int l=1,r=1;
	q[1]=0;
	int ans = -99999999;
	fo(i,1,n){
		while(l<=r && q[l]<i-m)l++;     // 刪除越界位置
 		ans = max
(ans, sum[i]-sum[q[l]]); // 取當前最優位置,更新答案 while(l<=r && sum[q[r]]>=sum[i]) r--; // 刪除無用位置 q[++r] = i; // 進入列隊的是下標 } cout<<ans<<endl; } int main(){ scanf("%d%d",&n,&m); fo(i,1,n){ scanf("%d",&a[i]); sum[i] = sum[i-1]+a[i]; } solve(); return 0; }