文章目录
- 🍊kadane
-
- 1.1最大子数组和(dp/前缀和🐱)
- [1.2 环形子数组的最大和](#1.2 环形子数组的最大和)
-
- [变形1) 环状最大两段子段和(dp)](#变形1) 环状最大两段子段和(dp))
- [变形2) 环状最大两段子段和](#变形2) 环状最大两段子段和)
🍊kadane
1.1最大子数组和(dp/前缀和🐱)

cpp
/*
法1:动态规划
dp[i]代表 以nums[i]结尾的连续子数组的最大和
时间复杂度:o(N)
*/
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n);
dp[0] = nums[0];
for(int i = 1; i < n; i++){
if(dp[i-1]>0) dp[i] = dp[i-1] + nums[i];
else dp[i] = nums[i];
}
int ans = dp[0];
for(int i = 1; i < n; i++){
ans = max(ans,dp[i]);
}
return ans;
}
};
法2:前缀和,🐱
cpp
/*
前缀和:
要求连续最大子数组和:比如i-j sum[j] - sum[i-1]
其实就是。找出一个最大差值 sum[j] - sum[i-1]
*/
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int maxSum = INT_MIN;
int minprevSum = 0; //最小的前缀和
int curSum = 0;
int n = nums.size();
for(int i = 0; i < n; i++){
curSum += nums[i];
maxSum = max(maxSum,curSum - minprevSum);
minprevSum = min(minprevSum, curSum);
}
return maxSum;
}
};
1.2 环形子数组的最大和


cpp
/*
直接将数组扩充一倍,想到了但是不会处理, 直接dp会导致某些数选两遍 是不对的
原来可以转换为两种情况:
(1)最大子数组不是环状的,其实就和上一题一样了
(2)这个子数组一部分在头,一部分在尾部,这种情况是重点
xxmmxx
mxm
最后的max就是数组综合剪掉中间的部分
注意:如果最小子数组就是整个数组的和,第二种情况就是0
如果此时maxSum>0会取maxSum
❕但是如果maxSum<0就会取0,这是不对的,此时答案应该为maxSum
*/
class Solution {
public:
int maxSubarraySumCircular(vector<int>& nums) {
int sum = 0, maxSum = nums[0], curMax = 0;
int minSum = nums[0], curMin = 0;
for(int x : nums){
curMax = max(curMax + x, x);
maxSum = max(curMax,maxSum);
curMin = min(curMin + x, x);
minSum = min(curMin, minSum);
sum += x;
}
if(maxSum <= 0) return maxSum;
else return max(maxSum, sum - minSum);
}
};
变形1) 环状最大两段子段和(dp)
题目描述
给出一段长度为 n n n 的环状序列 a a a,即认为 a 1 a_1 a1 和 a n a_n an 是相邻的,选出其中连续不重叠且非空的两段使得这两段和最大。
输入格式
第一行是一个整数 n n n,表示序列的长度。
第二行有 n n n 个整数,描述序列 a a a,第 i i i 个数字表示 a i a_i ai。
输出格式
一行一个整数,为最大的两段子段和是多少。
输入输出样例 #1
输入 #1
7
2 -4 3 -1 2 -4 3
输出 #1
9
说明/提示
数据规模与约定
对于全部的测试点,保证 2 ≤ n ≤ 2 × 10 5 2 \leq n \leq 2 \times 10^5 2≤n≤2×105, − 10 4 ≤ a i ≤ 10 4 -10^4 \leq a_i \leq 10^4 −104≤ai≤104。
dp:预处理,
preMax代表前i个数的最大子段和
postMax代表后i个数的最大子段和
然后最后枚举i,直接o(n)求出来结果
cpp
#include<bits/stdc++.h>
using namespace std;
long long x[1000010],preMax[1000010],postMax[1000010];
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>x[i];
preMax[1]=x[1];postMax[n]=x[n];
for(int i = 2; i <= n; i++){
preMax[i] = max(preMax[i-1] + x[i], x[i]); // 以当前节点为结尾的最大子段和
}
for(int i = 2; i <= n; i++){
preMax[i] = max(preMax[i-1], preMax[i]); // 不一定以i为结尾,i之前的,更新最大值
}
for(int i = n-1; i >= 1; i--){
postMax[i] = max(postMax[i+1] + x[i], x[i]); // 以当前节点为结尾的最大子段和
}
for(int i = n-1; i >= 1; i--){
postMax[i] = max(postMax[i+1], postMax[i]); // 不一定以i为结尾,i之后的,更新最大值
}
long long ans= preMax[1]+postMax[3];
for(int i=3;i<n;i++){
ans=max(ans,preMax[i-1]+postMax[i+1]);
}
cout<<ans;
return 0;
}
变形2) 环状最大两段子段和
题目描述
给出一段长度为 n n n 的环状序列 a a a,即认为 a 1 a_1 a1 和 a n a_n an 是相邻的,选出其中连续不重叠且非空的两段使得这两段和最大。
输入格式
第一行是一个整数 n n n,表示序列的长度。
第二行有 n n n 个整数,描述序列 a a a,第 i i i 个数字表示 a i a_i ai。
输出格式
一行一个整数,为最大的两段子段和是多少。
输入输出样例 #1
输入 #1
7
2 -4 3 -1 2 -4 3
输出 #1
9
说明/提示
数据规模与约定
对于全部的测试点,保证 2 ≤ n ≤ 2 × 10 5 2 \leq n \leq 2 \times 10^5 2≤n≤2×105, − 10 4 ≤ a i ≤ 10 4 -10^4 \leq a_i \leq 10^4 −104≤ai≤104。
cpp
#include<bits/stdc++.h>
using namespace std;
long long x[1000010],preMax[1000010],postMax[1000010];
long long preMin[1000010],postMin[1000010];
int main(){
int n;
cin>>n;
long long sum = 0;
for(int i=1;i<=n;i++){
cin>>x[i];
sum+=x[i];
}
preMax[1]=x[1];postMax[n]=x[n];
preMin[1]=x[1];postMin[n]=x[n];
for(int i = 2; i <= n; i++){
preMax[i] = max(preMax[i-1] + x[i], x[i]); // 以当前节点为结尾的最大子段和
preMin[i] = min(preMin[i-1] + x[i], x[i]); // 以当前节点为结尾的最小子段和
}
for(int i = 2; i <= n; i++){
preMax[i] = max(preMax[i-1], preMax[i]); // 不一定以i为结尾,i之前的,更新最大值
preMin[i] = min(preMin[i-1], preMin[i]); // 不一定以i为结尾,i之前的,更新最小值
}
for(int i = n-1; i >= 1; i--){
postMax[i] = max(postMax[i+1] + x[i], x[i]); // 以当前节点为结尾的最大子段和
postMin[i] = min(postMin[i+1] + x[i], x[i]); // 以当前节点为结尾的最小子段和
}
for(int i = n-1; i >= 1; i--){
postMax[i] = max(postMax[i+1], postMax[i]); // 不一定以i为结尾,i之后的,更新最大值
postMin[i] = min(postMin[i+1], postMin[i]); // 不一定以i为结尾,i之后的,更新最小值
}
long long ans= preMax[1]+postMax[2];
for(int i=2;i<n;i++){
ans=max(ans,preMax[i-1]+postMax[i+1]);
}
long long res = 0;
long long ans2= preMin[1]+postMin[2];
for(int i=2;i<n;i++){
ans2=min(ans2,preMin[i-1]+postMin[i+1]);
}
if(ans < 0) res = ans;
else{
res = max(ans, sum - ans2);
}
cout<<res;
return 0;
}