题目:输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,因此输出为该子数组和18以及起始的下标2,6。
分治策略,运行时间是nlogn
使用分治技术意味着我们要将子数组划分为两个规模尽量相等的子数组,也就是找到子数组的中央位置(比如mid),然后考虑求解两个子数组A[low..mid]和A[mid+1..high]。A[low..high]的任意连续子数组A[i..j]所处的位置必然是以下三种情况之一:
(a)完全位于子数组A[low..mid]中,因此low≤i≤j≤mid
(b)完全位于子数组A[mid+1..high]中,因此mid≤i≤j≤high
(c)跨越了中点,因此low≤i≤mid≤j≤high
所以我们需要求解这三种情况的最大子数组,然后再取最大值。A[low..mid]和A[mid+1..high]可以递归求解,剩下的就是训中跨越中点的最大子数组
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
void cross_sub(int a[],int low,int mid,int high,int &max_left,int &max_right,int &sum)
{
int sum1=0;
int left_sum=-65536;//假设为足够小
int right_sum=-65536;
for(int i=mid;i>=low;i--)
{
sum1=sum1+a[i];
if (sum > left_sum)
{
left_sum=sum1;
max_left=i;
}
}
sum1=0;
for(int i=mid+1;i<=high;i++)
{
sum1=sum1+a[i];
if (sum1 > right_sum)
{
right_sum=sum1;
max_right=i;
}
}
sum=left_sum+right_sum;
}
int max_sub(int a[],int low,int high,int &max_left,int &max_right,int &sum)
{
int left_sum=-65536;
int right_sum=-65536;
if(low==high)
{
max_left=low;
max_right=low;
sum=a[low];
}
else
{
int mid=(low+high)/2;
int left_low,left_high,right_low,right_high,cross_low,cross_high;
int left_sum,right_sum,cross_sum;
max_sub(a,low,mid,left_low,left_high,left_sum);
max_sub(a,mid+1,high,right_low,right_high,right_sum);
cross_sub(a,low,mid,high,cross_low,cross_high,cross_sum);
if(left_sum>right_sum&&left_sum>cross_sum)
{
sum=left_sum;
max_left=left_low;
max_right=left_high;
}
if(left_sum<right_sum&&right_sum>cross_sum)
{
sum=right_sum;
max_left=right_low;
max_right=right_high;
}
else
//if(left_sum<cross_sum&&right_sum<cross_sum)
{
sum=cross_sum;
max_left=cross_low;
max_right=cross_high;
}
}
}
int main()
{
int A[16]={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
int max_left,max_right,sum;
// 形参 int a[],int low,int high,int &max_left,int &max_right,int &sum
max_sub(A,0,15,max_left,max_right,sum);
printf("max_left,max_right,sum is%d %d %d",max_left,max_right,sum);
}