区间最大和问题

区间最大和

描述

给你一个长度为 \(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);
}

练习:最佳游览线路

posted @ 2024-04-13 14:31  飞花阁  阅读(43)  评论(0)    收藏  举报
//雪花飘落效果