算法:最大子段和问题

最大子段和是一个学习动态规划必学的问题,也是最基础的动态规划问题。

例题

洛谷1115 最大子段和

题目描述:
给出一段序列,选出其中连续且非空的一段使得这段和最大。

输入格式:
第一行:是一个正整数N,表示了序列的长度。
第二行:包含N个整数num[i],描述了这段序列。

输出格式:
一个整数,为最大的子段和是多少。

输入样例:

7
2 -4 3 -1 2 -4 3

输出样例:

4

最大子段和

对于最大子段和这个问题,其实我们发现如果说序列中所有的数都是正数,那么最大子段和一定是所有数的和,但为什么有时候不是呢?这就是负数捣的鬼。那么怎么解决负数呢?这就要用到动态规划了,提到DP,就一定就要去想:状态、转移方程、初值和答案了。

  1. 状态:dp[i]指在a[1] ~ a[i]这i个数中,结尾必须为a[i]这个数的最大子段和。
  2. 转移方程:dp[i] = max(0, dp[i - 1]) + a[i];
    其实这个的意思就是:如果说dp[i - 1]小于0,具体些就是以a[i - 1]这个数为结尾的最大子段和小于0的话,那么还不如说就不要前面的数,只留下一个a[i]呢。反之,若dp[i - 1]大于0,那么就让dp[i] = dp[i - 1] + a[i],这样可以更大些。(PS:其实就是分两种情况讨论,看哪种情况下答案更大就是了)。
  3. 初值:dp[0] = 0;
  4. 答案:max{dp[i]},因为不同子段结尾有a[1] ~ a[n]共n种情况,取其最大值就大功告成了。

最后算一下算法时间复杂度:只有一层循环,所以时间复杂度就是O(n)级别的了。

代码

# include <cstdio>
# include <cmath>
# include <cstring>
# include <algorithm>

using namespace std;

const int N_MAX = 200000;

int n;
int a[N_MAX + 10];
int dp[N_MAX + 10];

int maxSum()
{
	int ans = -2e9;
	for (int i = 1; i <= n; i++) {
		dp[i] = max(dp[i - 1], 0) + a[i];
		ans = max(ans, dp[i]);
	}
	return ans;
}

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	printf("%d
", maxSum());
	return 0;
}
原文地址:https://www.cnblogs.com/000zwx000/p/12467968.html