区间最大和问题
区间最大和
描述
给你一个长度为 \(n\) 的数列,请求出其中一段连续的数之和,最大是多少?
样例输入
8
7 -4 2 5 -20 3 -9 2
样例输出
10
区间问题,通常涉及到区间的左右端点,我们只需枚举右端点即可。
思路1
利用前缀和的原理,\(s[i]\) 表示以 \(i\) 作为右端点的前缀和,那么,以 \(i\) 作为右端点,左端点是 \(k\) 的区间和就是 \(s[i]-s[k-1]\),但是这里不知道 \(k\) 的值,我们就需要找到一个最小的前缀和,使得减去之后的差值最大,也就是 \(s[i]-min(s[j]\;|\;1\le j\lt i)\)。
令 \(mn = min(s[j]\;|\;1\le j\lt i)\),则每一轮需要执行 mn = min(mn, s[i]) 来更新 \(mn\) 。
#include <iostream>
using namespace std;
const int N = 2e4 + 7;
int s[N], mn;
int main() {
int n, ans = 0;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", s + i);
s[i] += s[i-1];
ans = max(ans, s[i] - mn);
mn = min(mn, s[i]);
}
printf("%d", ans);
}
思路2
考虑前缀和的求法:\(s[i]=s[i-1]+a[i]\),我们将 \(s[i]\) 的意义改为:以 \(i\) 为右端点的区间和的最大值。
可以得出,若 \(s[i-1]\gt 0\),那么以 \(i\) 为右端点的最大区间和肯定不能包含 \(s[i-1]\) 的部分,否则可以。
#include <iostream>
using namespace std;
const int N = 2e4 + 7;
int s[N], mn;
int main() {
int n, ans = 0;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", s + i);
if (s[i-1] > 0)
s[i] += s[i-1];
ans = max(ans, s[i]);
}
printf("%d", ans);
}
对以上代码可以做一下优化如下:
#include <iostream>
using namespace std;
const int N = 2e4 + 7;
int main() {
int n, ans = 0, s = 0, last = 0;
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &s);
if (last > 0)
s += last;
last = s;
ans = max(ans, s);
}
printf("%d", ans);
}

浙公网安备 33010602011771号