今天,在做PAT的一道题时遇到了一道题,其要解决的问题是如何在一个序列中,找出其拥有最大和的连续子列,例如:
{−2,11,−4,13,−5,−2}
得出:
{11,−4,13}
首先会想到的一种方法,很暴力很慢,那就是用两个循环将子序列的首尾确定然后在用一个循环进行 ∑ ,于是就写出了如下代码:
#include <iostream>
#include <limits.h>
using namespace std;
int k;
int main ()
{
int maxSum = INT_MIN, start = 0, end = 0;
int arr[10000];
cin>>k;
for (int i = 0; i < k; i++)
cin>>arr[i];
for (int i = 0; i < k; i++) {
for (int j = i; j < k; j++) {
int sum = 0, x = i;
for (; x <= j; x++)
sum += arr[x];
if (maxSum < sum) {
maxSum = sum;
start = arr[i];
end = arr[x - 1];
}
}
}
cout<<maxSum<<" "<<start<<" "<<end<<"\n";
return 0;
}
这个算法复杂度显而易见为
O(n3)
,这种复杂度实现是太高,我们需要来改进下,我们不如将
∑
的过程给提前保存在下,也就是在cin
时,就顺便生成一个存储前面i
位和的数组,这样就可以将复杂度降到
O(n2)
:
#include <iostream>
#include <limits.h>
using namespace std;
int k;
int main ()
{
int maxSum = INT_MIN, start = 0, end = 0;
int arr[10000];
int sum[10000];
cin>>k;
sum[0] = 0;
for (int i = 0; i < k; i++) {
cin>>arr[i];
sum[i + 1] = sum[i] + arr[i];
}
for (int i = 1; i <= k; i++) {
for (int j = i; j <= k; j++) {
int temp = sum[j] - sum[i - 1];
if (temp > maxSum) {
maxSum = temp;
start = arr[i - 1];
end = arr[j - 1];
}
}
}
cout<<maxSum<<" "<<start<<" "<<end<<"\n";
return 0;
}
这样做的话就讲我们的复杂度降下了一个次幂(ps: 没想到PAT测试通过了,并没有运行超时),那么还有没有机会将复杂度降到线性的
O(n)
呢?这个肯不可以用动态规划来做呢?首先我们来考虑一下状态转移的函数,很容易得出:
sum[i]=max{sum[i−1]+a[i],a[i]}
sum[i] 代表的就是以 a[i] 做为结尾时的最大和,代码也是很容易写出来:
#include <iostream>
#include <limits.h>
using namespace std;
int k;
int main ()
{
int maxSum = -0xfffffff, start = 0, end = 0, sum = -0xfffffff, s = 0;
int arr[10000];
cin>>k;
arr[0] = 0;
for (int i = 1; i <= k; i++) {
cin>>arr[i];
if (sum + arr[i] < arr[i]) {
sum = arr[i];
s = i;
} else
sum += arr[i];
if (sum > maxSum) {
maxSum = sum;
end = arr[i];
start = arr[s];
}
}
cout<<maxSum<<" "<<start<<" "<<end<<"\n";
return 0;
}
这个问题同样可以被引申为买卖股票的问题。