问题描述
对于给定数组,找出数组中和最大的非空连续子序列。
输入格式
输入的第一行包括一个整数 n n n,代表数组中的元素个数,接下来的一行包含 n n n个整数,其间以空格分隔。
输出格式
输出第一行为一个整数,表示最大的连续子序列的和。输出第二行为最大的连续子序列,其间以空格分隔,行末不得有空格。
输入样例
9
2 4 -7 5 2 -1 2 -4 3
输出样例
8
5 2 -1 2
解题思路
我们知道一个数加上一个正数一定会让这个数变得更大,所以如果一个序列的和为正数,一个数加上这个序列和一定会让这个数变得更大。
基于以上的思想,我们在面对一个数的时候,需要知道之前的序列和是多少,如果这个和是正数,那么就将这个数据合并进序列;如果这个和是负数,那么就将这个数作为一个新的序列。我们将每个数都按照以上的方式放入序列,并且将它们放入后序列的和都记录下来形成一个记录数组,那么数组中的最大值就是最大连续子序列的和。
我们在填写记录数组的时候,只是对当前的记录进行操作,而不会改变之前的记录,所以我们可以边填写记录数组边寻找最大值,即把之前记录的最大值与当前记录的最大值进行比较,将大的作为新的最大值。
现在我们使用一个记录数组和一个最大值记录将原序列中的数依次查看一边就可以找到最大子序列的和。问题已经得到解决,并且时间复杂度是
Θ
(
n
)
\Theta(n)
Θ(n),现在我们可以试着降低空间复杂度。在我们查看原数列中的一个数的时候,我们要完成填写当前记录和更新最大值两个操作,填写当前记录时我们只需要上一次记录的值而不是之前所有记录的值,更新最大值时我们只需要当前记录的值即可,所以每次查看我们只需要上一次记录的值即可完成所有操作。因此,我们就不需要一个记录数组来把之前所有的记录全部存储下来,而是只使用一个变量来存储上一次记录的值。如此,空间复杂度也极大的降低。
测试代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main(){
size_t n;
cin >> n;
vector<int> a(n);
for(size_t i=0;i<n;i++){///读入数据
cin >> a[i];
}
int t = a[0];///为上一次记录赋初值
int m = a[0];///为最大值赋初值
for(size_t i=1;i<n;i++){
if(t<0){///上一次记录的序列值小于零需将序列舍弃
t = a[i];
}else{///上一次记录的序列值大于零将当前数加入序列
t += a[i];
}
m = max(t,m);///寻找最大值
}
cout << m << endl;
return 0;
}
#include <iostream>
#include <vector>
using namespace std;
int main(){
size_t n;
cin >> n;
vector<int> a(n);
for(size_t i=0;i<n;i++){
cin >> a[i];
}
int t = a[0];///为上一次记录赋初值
int m = a[0];///为最大值赋初值
size_t p = 0;///记录最大连续子序列的起始位置
size_t q = 0;///记录最大连续子序列的结束位置
size_t s = 0;///记录子序列的起始位置
for(size_t i=1;i<n;i++){
if(t<0){///上一次记录的序列值小于零
t = a[i];///将原序列舍弃
s = i;///记录当前序列的起始位置
}else{///上一次记录的序列值大于零
t += a[i];///将当前数加入序列
}
if(t>m){///发现新的最大子序列
m = t;///更新最大值
p = s;///记下该序列的起始位置
q = i;///记下该序列的结束位置
}
}
cout << m << endl;
cout << a[p++];///根据起始和结束位置输出最大连续子序列
while(p<=q){
cout << ' ' << a[p++];
}
cout << endl;
return 0;
}