最大子序和(單調列隊)
阿新 • • 發佈:2018-11-21
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;
}