1. 程式人生 > 實用技巧 >迴圈陣列最大子段和(帶限制的最大子段和)

迴圈陣列最大子段和(帶限制的最大子段和)

題目連結:here

題意:N個整陣列成的迴圈序列a[1],a[2],a[3],…,a[n],求該序列如a[i]+a[i+1]+…+a[j]的連續的子段和的最大值(迴圈序列是指n個數圍成一個圈,因此需要考慮a[n-1],a[n],a[1],a[2]這樣的序列)。當所給的整數均為負數時和為0。 例如:-2,11,-4,13,-5,-2,和最大的子段為:11,-4,13。和為20。 題解:對於不帶限制的最大欄位和我們可以:求一遍字首和,求出最大值最小值,最後結果 res = max( MAX, SUM - MIN ); 那麼對於這道題:我們可以維護一個字首和 ,然後結果就是 max( sum[i] - min(sum[j]) ), i-len <= j <= i-1 && 1<=i <= 2*n
,注意這裡j>=i-len,而不是i-len+1,因為字首和相減的時候要注意會把j位置上的那個數也減去)。然後這麼看來 是n^2的dp,那麼如何優化呢。 可以預處理 長度為len的區間的最小值 然後對於每個i 查詢前面區間長度為 len 的最小值 然後 sum[i] - sum[j]即可 我們可以維護一個單調佇列,單調佇列要滿足兩點:
  • 隊內元素的位置 要符合 \(i\)的區間要求 即 i-len <=que[j]<=i-1
  • 單調性,佇列內的元素 儘可能小(因為我們要減去最小值呀),維護一個單調非遞增佇列(遞減或持平)

AC_Code:

 1 #include <iostream>
 2
#include <cstdio> 3 #include <algorithm> 4 #include <string> 5 #include <map> 6 #include <cstring> 7 #include <vector> 8 #define inf 0x3f3f3f3f3f 9 typedef long long ll; 10 using namespace std; 11 const int maxn = 5e4+10; 12 13 int n; 14 ll s[maxn<<1],sum[maxn<<1
]; 15 int q[maxn<<1]; 16 17 int main() 18 { 19 cin>>n; 20 int len = n; 21 for(int i=1;i<=n;i++){ 22 cin>>s[i]; 23 s[i+n] = s[i]; 24 } 25 n<<=1; 26 for(int i=1;i<=n;i++) sum[i] = sum[i-1]+s[i]; 27 ll res=0; 28 int st=0,ed=0; 29 for(int i=1;i<=n;i++){ 30 if( i<=len ) res=max(res,sum[i]); 31 32 while( st<ed && q[st]<i-len ) st++; 33 res = max(res,sum[i]-sum[ q[st] ]); 34 while( st<ed && sum[i]<sum[ q[ed-1] ]) ed--; 35 q[ed++] = i; 36 } 37 cout<<res<<endl; 38 return 0; 39 }

參考部落格:here