问题描述
给定(可能是负的)整数序列A1, A2,...,AN, 寻找(并标识)使Sum(Ak)(k >=i, k <= j)的值最大的序列。如果所有的整数都是负的,那么连续子序列的最大和是那个最大的负数项。最好给出给出最大和连续子序列!!
1 暴力破解法
这个问题有一个最简单直接的穷举解决法。我们看问题,既然要求里面最大的连续子序列。那么所有的连续子序列将由哪些组成呢?以数组的第一个元素为例,连续子序列必须是至少包含元素A1,也可能包含从A1到A2...以及从A1到AN。这样就有N种可能。后面的元素也按照这样类似的办法,以该元素开始,包含该元素的单元素数组,两个元素数组...直到包含数组末尾的数组。
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;
int main()
{
vector<int> array;
for(int i=0;i<20;i++)
{
int j = 50-random()%100; //-49~50
array.push_back(j);
}
for(vector<int>::iterator it=array.begin();it!=array.end();++it)
{
cout<<*it<<" ";
}
cout<<endl;
int maxsum=-50; //注意初始化
int low=0;
int high=0;
for(int i=0;i<array.size();i++)
{
int sum=0;
for(int j=i;j<array.size();j++)
{
sum+=array[j];
if(sum>maxsum)
{
maxsum=sum;
low=i;
high=j;
}
}
}
cout<<"subarray:"<<array[low]<<"~"<<array[high]<<endl;
cout<<"maxsun="<<maxsum<<endl;
return 0;
}结果:
时间复杂度分析:两重循环遍历,复杂度为O(n^2)
2 分治策略(递归法)
对这个问题,有一个相对复杂的O(NlogN)的解法,就是使用递归。如果要是求出序列的位置的话,这将是最好的算法了(因为我们后面还会有个O(N)的算法,但是不能求出最大子序列的位置)。该方法我们采用“分治策略”(divide-and-conquer)。
在我们例子中,最大子序列可能在三个地方出现,或者在左半部,或者在右半部,或者跨越输入数据的中部而占据左右两部分。前两种情况递归求解,第三种情况的最大和可以通过求出前半部分最大和(包含前半部分最后一个元素)以及后半部分最大和(包含后半部分的第一个元素)相加而得到。
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;
/*
* find a subarray crossing the mid with the biggist sum
*/
int find_max_crossing_subarray(vector<int> array,int low, int mid, int high)
{
int left_maxsum=-50; //for sum
int right_maxsum=-50; //for sum
int sum =0;
for(int i= mid;i>= low;--i)
{
sum += array[i];
if(sum > left_maxsum)
{
left_maxsum=sum;
}
}
sum=0;
for(int i=mid+1;i<=high;++i)
{
sum += array[i];
if(sum > right_maxsum)
{
right_maxsum=sum;
}
}
return(left_maxsum+right_maxsum);
//cout<<"maxsum_crossing_mid"<<left_maxsum+right_maxsum<<endl;
}
int max(int a, int b, int c)
{
if(a>=b && a>=c)
return a ;
else if(b>=a && b>=c)
return b;
else
return c;
}
/*
* find a subarray on the left or right side of mid
*/
int find_max_subarray(vector<int> array, int low, int high)
{
if(low==high)
{
return array[low];
}
int mid = int((low+high)/2);
int low_maxsum = find_max_subarray(array, low, mid);
int high_maxsum = find_max_subarray(array, mid+1, high);
int crossing_mid_maxsum = find_max_crossing_subarray(array, low, mid, high);
return max(low_maxsum, high_maxsum, crossing_mid_maxsum);
}
int main()
{
vector<int> array;
for(int i=0;i<10;i++)
{
int j = 50-rand()%100; //-49~50
array.push_back(j);
}
for(vector<int>::iterator it=array.begin();it!=array.end();++it)
{
cout<<*it<<" ";
}
cout<<endl;
int maxsum =find_max_subarray(array, 0, array.size()-1);
cout<<"the maxsum="<<maxsum<<endl;
//cout<<"range from"<<array[a[1]]<<"to"<<array[a[2]]<<endl;
return 0;
}
3 增量算法(插入算法)
假设已知A[1~N]的最大顺序子序列和,那么对于A[1~N+1]序列,增加一个项,最大顺序子序列和会在哪里产生呢?(1)不变,A[N+1]项不影响(2)在包括A[N+1]项往前的子序列中产生。
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;
int enlarge(vector<int> array,int maxsum, int i)
{
if(i>array.size() || i<=0)
return -1;
int sum=0;
for(int j=i;j>=0;--j)
sum += array[j];
if(sum>maxsum)
maxsum=sum;
return maxsum;
}
int find_maxsum(vector<int> array)
{
if(array.empty())
return -1;
int maxsum=array.at(0);
for(int i=1;i<=array.size()-1;++i)
{
int result = enlarge(array, maxsum, i);
if(result>maxsum && result != -1)
maxsum = result;
}
return maxsum;
}
int main()
{
vector<int> array;
for(int i=0;i<10;++i)
array.push_back(10-rand()%20);
for(vector<int>::iterator it= array.begin();it != array.end();++it)
cout<<*it<<" ";
cout<<endl;
int max = find_maxsum(array);
cout<<"maxsum ="<<max<<endl;
return 0;
}
复杂度 O(n^2)
4 最优解(线性复杂度 O(N))
参考:http://www.cnblogs.com/CCBB/archive/2009/04/25/1443455.html
//线性的算法O(N)
long maxSubSum4(const vector<int>& a)
{
long maxSum = 0, thisSum = 0;
for (int j = 0; j < a.size(); j++)
{
thisSum += a[j];
if (thisSum > maxSum)
maxSum = thisSum;
else if (thisSum < 0)
thisSum = 0;
}
return maxSum;
}连续子序列最大和问题的四种经典解答,布布扣,bubuko.com
原文地址:http://blog.csdn.net/hustyangju/article/details/25218401