最大子数组和的扩展问题
提出问题:
1,要求返回最大子数组的起始结束位置;
2,如果数组(A[0],A[1].....A[n-1])首尾相连,也就是我们允许找到一段数字(A[i],A[i+1]...A[n-1],A[0],A[1]...A[j]),求其最大子数组和;
对于问题1的解法:
返回始末位置还是比较容易的,我们知道,每当当前子数组和的小于0时,便是新一轮子数组的开始,每当更新最大和时,便对应可能的结束下标,这个时候,只要顺便用本轮的起始和结束位置更新始末位置就可以,程序结束,最大子数组和以及其始末位置便一起被记录下来了,代码如下:
void Maxsum_location(int * arr, int size, int & start, int & end)
{
int maxSum = -INF;
int sum = 0;
int curstart = start = 0; /* curstart记录每次当前起始位置 */
for(int i = 0; i < size; ++i)
{
if(sum < 0)
{
sum = arr[i];
curstart = i; /* 记录当前的起始位置 */
}else
{
sum += arr[i];
}
if(sum > maxSum)
{
maxSum = sum;
start = curstart; /* 记录并更新最大子数组起始位置 */
end = i;
}
}
}
对于问题2的解法:
可以把问题分解为两种情况,
1,解没有跨过A[n-1]到A[0](这就是原问题)
2,解跨过A[n-1]到A[0]
对于第 2种情况有两种可能:
a)包含整个数组即(A[0],A[1]....A[n-1])
b)包含两个部分:从A[0]开始的一段(A[0],A[1]...A[j],0<=j<=n),以及以A[n-1]结尾的一段(A[i],A[i+1]...A[n-1],j<i<n)
情形b相当于从数组中删除掉一段(A[j+1],A[j+2]...A[i-1])。这一部分的和一定是负的,并且是找到的子数组的和为负数且绝对值最大。寻找这样一个子数组的问题也是和原问题一样的只不过问题变成了求最小的子数组和了。最后求两者的最大值就可以了,只需要遍历数组两次,所以该算法的时间复杂度为O(n)。
#include<cstdio>
#define MAX 100005
using namespace std;
int Array[MAX];
int MaxSum(int n)
{
int i,sum=0;
int max=-0xFFFFFFF;
sum+=Array[0];
for(i=1;i<n;i++)
{
if(sum<0)
sum=Array[i];
else
sum+=Array[i];
if(sum>max)
max=sum;
}
return max;
}
int MinSum(int n)
{
int i,sum=0;
int min=0xFFFFFFF;
sum+=Array[0];
for(i=1;i<n;i++)
{
if(sum>0)
sum=Array[i];
else
sum+=Array[i];
if(sum<min)
min=sum;
}
return min;
}
int main(int argc,char *argv[])
{
int n;
while(scanf("%d",&n)!=EOF)
{
int sum=0;
for(int i=0;i<n;i++)
{
scanf("%d",&Array[i]);
sum+=Array[i];
}
int ans;
int maxsum=MaxSum(n);
int minsum=MinSum(n);
if(maxsum<0)//all numbers are negative
ans=0;
ans=maxsum>(sum-minsum)?maxsum:(sum-minsum);
printf("%d\n",ans);
}
return 0;
}