# MCS
Maximum Consecutive Sum
## (1)無要求範圍
1. 動態規劃
轉移方程式
```cpp=
dp[i]=max(dp[i-1]+a[i],a[i]);
ma=(ma,dp[i]);
```
dp[i]為以a[i]為子序列末端最大連續元素和(對於每個a[i]可以是新的開頭或延伸前面的子序列,兩者取最大為此節點最佳解),在dp的時候要不斷更新最大值
最精簡寫法:
```cpp=
int dp=0,ma=INT_MIN;
for(int i=1;i<=n;i++){
cin >> v;
dp=max(dp+v,v);
ma=max(ma,dp);
}
```
2. 累積遍歷演算法
由上述dp狀態轉移可知dp[i]是由
```cpp=
1. dp[i-1]+v[i]
2. v[i]
```
取最大值而來,當2比較大時
```cpp=
dp[i-1]+v[i] < v[i]
```
同減v[i]
```cpp=
dp[i-1] < 0
```
代表前面的和小於0的時候dp[i]就是自己會是最佳解
所以我們可以推得code=
```cpp=
for(int i=1;i<=n;i++){
dp[i]=dp[i-1]+v[i];
ma=max(ma,dp[i]);
//要先取max避免只有負數的時候先被歸0就取不到負數了
if(dp[i]<0) dp[i]=0;
}
```
<b>結論:當累積的前綴和小於0時,就把它歸零,因為對未來的結果沒有貢獻</b>
### 注意:此結論不可用於有限制數量和環狀陣列的題目
反例:
限制數量k:
[1 3 2 -3 999 -1]
k=3
最好[999]
環狀陣列:
[5 -3 5] 往後蓋...5 -3 5
最好[5,5]
## (2)最大連續元素和要求為K個
利用前綴合直接計算
```cpp=
for(int i=1;i<=N;i++){
cin >> v[i];
sum[i]=sum[i-1]+v[i];
}
int ma=INT_MIN;
for(int l=0,r=k;r<=N;l++,r++){
int cachesum=sum[r]-sum[l];
ma=max(ma,cachesum);
}
```
## (3)最大連續元素和要求最大為K個
1. 計算前綴和
2. 遍歷數列
3. 利用set建立一個單調對列(set可自動排序方便找最小值)
4. 維護單調對列(移除過期元素),對列內元素數最大為K個
5. 在單調對列裡找出最小前綴和(*set.begin()),減掉當前前綴和就是以v[i]為結尾在往前K個內的最大連續元素和
6. 更新最大值
7. 更新單調對列
```cpp=
set<int> s;
s.insert(0);
for(int i=1;i<=n;i++){
if(i-k>0) s.erase(prefixsum[i-k-1]);
cachesum=prefixsum[i]-*s.begin();
ans=max(ans,cachesum);
s.insert(prefixsum[i]);
}
```
pq優化
```cpp=
priority_queue<pii,vector<pii>,greater<pii>> pq;
pq.push({0,0});
for(int i=1;i<=n;i++){
while(!pq.empty() && pq.top().S<i-k) pq.pop();
ans=max(ans,pre[i]-pq.top().F);
pq.push({pre[i],i});
}
```
## (4)環形陣列(circular subarray)
LeetCode:918. Maximum Sum Circular Subarray
#### solution 1
環形子陣列有兩種情況
1. 最大連續元素和在頭尾內
2. 最大連續完素和有包括頭尾
第一種就利用dp方法即可求得最大值,第二種利用總和減掉區間最小值(也適用dp方法)就是過頭尾的區間最大值
時間複雜度只有O(n)
```cpp=
class Solution {
public:
int maxSubarraySumCircular(vector<int>& A) {
int maxnow=0;
int minnow=0;
int total=0;
int maxans=INT_MIN;
int minans=INT_MAX;
int maxinteger=INT_MIN;
for(int i=0;i<A.size();i++){
maxinteger=max(maxinteger,A[i]);
total+=A[i];
//求區間最大值
maxnow+=A[i];
maxans=max(maxans,maxnow);
if(maxnow<0) maxnow=0;
//求區間最小值
minnow+=A[i];
minans=min(minans,minnow);
if(minnow>0) minnow=0;
}
int ans=max(maxans,total-minans);
if(maxinteger<0) return maxinteger;
else return ans;
}
};
```
有個例外:當A全為負數時,區間最小元素和會等於總和,這樣相減等於0,和區間最大取max會是0,所以應該要先判斷,如果全是負數就回傳陣列裡的最大值才是答案
#### solution 2
#優先對列
把[0,n-1]複製到[n,2n-1]並且把前綴和算出來,這樣就可以用上面(3)來計算,只是把k改成n個,因為不可以重複選
```cpp=
#define pii pair<int,int>
#define F first
#define S second
class Solution {
public:
int maxSubarraySumCircular(vector<int>& nums) {
int n=nums.size();
vector<int> pre(2*n+1);
for(int i=0;i<n*2;i++){
pre[i+1]=pre[i]+nums[i%n];
}
int ans=-1e9;
priority_queue<pii,vector<pii>,greater<pii>> pq;
pq.push({0,0});
for(int i=1;i<=2*n;i++){
while(!pq.empty() && pq.top().S<i-n) pq.pop();
ans=max(ans,pre[i]-pq.top().F);
pq.push({pre[i],i});
}
return ans;
}
};
```
#### solution 3
#單調對列
#### solution 4
#線段樹
#### solution 5
#sparse table