这个题目最早在13年阿里笔试出现,直到前两天面试另一家电商又出现,哎,欠的都是要还的。
这个问题的思路如下:一维数组的下标连续的元素构成连续子数组,求所有连续子数组中和最大的那个子数组。
解析:2018-11-08
1 首先这个问题要转化为Q(n)的问题,对于Q(n)的问题要转化成Q(n) = Q(n-1) + An的问题。
2 只要能意识到这一点,其实就可以有希望在O(n)的时间内解决,不然,那就复杂的无法解决(因为你无法通过普通的循环遍历来做到,就算做到了,那复杂性也是难以承受的)。
3 上面的思想本质上还是一维动态规划算法。按照地归解分类讨论一下即可。
思路一旦确定,解的时候只需要更新各个临时变量和下标即可
解释(如上图所示):
1 Q(n)表示元素为n的数组其最优解(其下标范围为[i, j))
2 元素n+1为数组的第n+1个元素(其下标范围为n1)
3 A2为Q(n)产生时的一个子序列,这个序列的是Q(n)右侧和n+1之间的所有元素构成的集合,该序列的和一定是负值(下标范围为[j, n1))
4 A1为Q(n)产生时的Q(n)左侧的序列,该序列的和一定是负值
5 A1无需考虑,我们主要考虑加入第n+1元素时,新的Q(n+1)可能是那种情况即可。这里可能情况有四种。属于列举法解决问题。
6 首先考虑新的序列Q(n+1)会不会变的更长,也就是A2+(n+1)会不会也加入进来,这个是我们的直觉。这个时候会不会加进来主要看这个序列的和是不是大于零。也就是A2 + (n+1) > 0 ? 大于零也不是就一定要加上Q(n)。因为Q(n)可能是个负数,加了只会更小,这时候就不能加入,而A2又是和为负数,所以新的Q(n+1)只能是第n+1这个元素自己。这里的讨论只是以其中的一种情况为例讨论的。其余三种情况类似,参考上图即可。
7 本题的难点就在于列举出各种情况,很容易考虑不周,我就见过有人写的代码过于简单,考虑不周的。而且也写出来,这个是误人子弟(当然读者自己要去验证和辨别)
8 本题的第二个难点在于标记变量的设置和在各种情况下的更新。如果你把上图先画出来,对着图写程序,还是很简单的(即便这样,我的A2更新还是出过问题,改了两次才改好)。一定要严格定义上图中各个集合的范围,这时候他们在各种情况下的更新时机才会比较清晰,代码才不会写BUG。
代码:
#include <cassert>
#include <iostream>
#include <vector>
using namespace std;
int MaxSumOfContinuousSubSequence(const vector<int>& a, int& i, int& j)
{
if (a.size() == 0)
{
throw "sequence must has one element at least!";
}
if (a.size() == 1)
{
return a[0];
}
//各个集合及其下标范围
//Q(n)[i,j) 上一次的最优解与下标范围
//A2[j,n1) 上一次的最优解与当前第n+1个元素之间的序列(A2各元素和总是负值)
//n1[n+1,n+2) 当前Q(n+1)比Q(n)多出来的那个新元素
i = 0;//Q(n)的起始下标
j = 1;//A2的起始下标
int n1 = 1;//n1的起始下标
int currentMaxSum = a[0];
int sumA2 = 0;
for (n1 = 1; n1 < a.size(); ++n1)
{
if (sumA2 + a[n1] >= 0)
{
if (currentMaxSum > 0)
{
currentMaxSum = currentMaxSum + sumA2 + a[n1];
sumA2 = 0;
j = n1 + 1;
}
else
{
currentMaxSum = a[n1];
sumA2 = 0;
i = n1;
j = n1 + 1;
}
}
else
{
if (currentMaxSum > a[n1])
{
currentMaxSum = currentMaxSum;
sumA2 = sumA2 + a[n1];
}
else
{
currentMaxSum = a[n1];
sumA2 = 0;
i = n1;
j = n1 + 1;
}
}
}
return currentMaxSum;
}
void test_max_sub_sum(const vector<int>& a)
{
cout << "sequence ";
for each (auto var in a)
{
cout << var << " ";
}
cout << endl;
int begin, end;
cout << "MaxSumOfContinuousSubSequence " << MaxSumOfContinuousSubSequence(a, begin, end) << endl;
for (int i = begin; i < end; ++i)
{
cout << a[i] << " ";
}
cout << endl<<endl;
}
int main()
{
vector<int> a1 = { -1, 100, -200, 13};
test_max_sub_sum(a1);
vector<int> a2 = { -1, 100, -200, 201};
test_max_sub_sum(a2);
vector<int> a3 = { -1, 100, -200, 201, -20, -20, -20, 61};
test_max_sub_sum(a3);
vector<int> a4 = { -1, 100, -200, 201, -20, -20, -20, 61, -104, 103};
test_max_sub_sum(a4);
vector<int> a5 = { -1, 100, -200, 201, -20, -20, -20, 61, -104, 103, 1};
test_max_sub_sum(a5);
return 0;
}
输出: