一、问题描述
输入
- 第一行包含一个正整数 N,表示序列中数字的数量。
- 第二行包含 N 个整数,表示序列中的数。
输出
- 对于每一组输入数据,输出一个数,表示最大序列和。
- 如果序列中所有数都是负数,最大序列和为 0。
规定
- 一个序列的最大连续子序列和至少是 0。
- 如果序列和小于 0,则结果为 0。
- 输入可能包括多组数据,对于每一组输入数据,仅输出一个数,表示最大序列和。
变量条件
- N 为正整数,N ≤ 1,000,000。
- 结果序列和在范围 (-2^63, 2^63 - 1) 以内。
示例
输入
5
1 5 -3 2 4
6
1 -2 3 4 -10 6
4
-3 -1 -2 -5
输出
9
7
-1
说明
- 给定一个整数序列 S,其中有 N 个数。
- 定义其中一个非空连续子序列 T,其中所有数的和为 T 的“序列和”。
- 对于 S 的所有非空连续子序列 T,求最大的序列和。
二、问题求解
问题概述
我们有一个数字序列,例如 1, -2, 3, 4, -10, 6。我们的目标是找出这个序列中连续数字相加得到的最大和。
动态规划数组 dp
我们使用一个数组 dp
来存储每个位置 i
的最大连续子序列和,即 dp[i]
表示以第 i
个数结尾的最大连续子序列和。
处理方法
对于序列中每个位置的数字,我们有两种处理方式:
- 如果
dp[i]
大于 0,这意味着加上它后面的数字会使子序列和增大,因此我们保留这个数字。 - 如果
dp[i]
小于 0,这意味着加上它后面的数字会使子序列和减小,所以我们舍弃前面的数字,并从第i+1
个数字开始重新计算连续子序列和。
状态转移方程
对于每个位置 i
,我们更新 dp[i]
的值:
- 如果
dp[i-1] + a[i]
大于a[i]
,则dp[i]
更新为dp[i-1] + a[i]
,表示保留前面的数字。 - 否则,
dp[i]
更新为a[i]
,表示从当前数字重新开始计算。
算法步骤
- 从序列的第二个数字开始遍历。
- 对于每个数字,我们计算
dp[i]
:- 如果
dp[i-1] + a[i]
大于a[i]
,则说明前面的数字有助于增大子序列和,更新dp[i]
为dp[i-1] + a[i]
。 - 否则,
dp[i]
更新为a[i]
,表示从当前数字重新开始计算子序列和。
- 如果
- 遍历结束后,
dp
数组中的最大值就是整个序列的最大连续子序列和。
寻找最大值
在遍历过程中,我们可以使用一个整型变量来不断更新并记录 dp
数组中的最大值。
三、代码
#include <iostream>
using namespace std;
#define MAXN 20
int dp[MAXN]; // 定义一个全局数组dp,用于存储到每个位置为止的最大连续子序列和
// 函数用于计算最大连续子序列和
void max_sub_sum(int a[], int len) {
dp[0] = 0; // 初始化dp数组的第一个元素为0
for (int i = 1; i <= len; i++) {
dp[i] = max(dp[i - 1] + a[i - 1], a[i - 1]); // 状态转移方程
}
}
// 函数用于展示最大连续子序列和及其对应的子序列
void show_result(int a[], int len) {
int maxi = 0; // 用于记录最大连续子序列和对应的索引
for (int i = 1; i <= len; i++) {
if (dp[i] > dp[maxi]) {
maxi = i; // 更新最大连续子序列和对应的索引
}
}
int str; // 用于记录最大连续子序列的起始位置
for (str = maxi; str >= 0; str--) {
if (dp[str] < 0) {
break; // 从后向前找到第一个使得子序列和小于0的位置
}
}
cout << "求解结果" << endl;
cout << "\t最大连续子序列和:" << dp[maxi] << endl; // 输出最大连续子序列和
cout << "\t最大连续子序列:"; // 输出最大连续子序列
for (int i = str + 1; i <= maxi; i++) {
cout << a[i - 1] << " "; // 打印子序列
}
cout << endl;
}
int main() {
int a[] = { -2, 11, -4, 13, -5, -2 }; // 定义一个数组a,存储序列
int len = sizeof(a) / sizeof(a[0]); // 计算数组a的长度
max_sub_sum(a, len); // 调用函数计算最大连续子序列和
show_result(a, len); // 调用函数展示结果
return 0;
}
时间复杂度
由于算法仅包含一重循环,时间复杂度为:T(n) = O(n)