1.问题描述:给定一个数组,找出其中可以构成最大数的子段,子段和是由连续的子段构成的;给定有n个整数(可能为负整数)组成的序列a1,a2,...,an,求该序列连续的子段和的最大值。
2.设计思路:对于一个连续的子段和,如果把数组一分为二,那么最大的和有三种情况:在center的左边、右边,左右结合;所以只需要分别求出这三种情况的和,进行比较得到最大的和即可;其中的递归出口为right=left,也就是小子段中只剩下一个元素时结束递归。
3.代码:
/*二分法解决最大子段和问题*/
#include<stdio.h>
#include<math.h>
int array[6]={-20,11,-4,13,-5,-2}; //定义有六个元素的数组,保存信息
int array2[9]={-1,4,-3,1,5,-1,4,-5,2};
int bigger(int a,int b) //自定义一个求解最大数的函数
{
return a>b ? a:b;
}
int SubSegmentSum(int array[],int left,int right)
{
int sum=0,leftsum=0,midsum=0,rightsum=0; //分别保存最后的sum、左边、中间、右边的最大子段和
int center; //中间分界线
int s1,lefts,s2,rights; //记录最大子段和为两边时的和
if(left==right)
{
sum=array[left]; //只有一个元素时sum=array[left]或者array[right]
}
else{
center=(left+right)/2;
//printf(" %d\n",center);
leftsum= SubSegmentSum(array,left,center); //求左边的最大子段和
rightsum=SubSegmentSum(array,center+1,right); //求右边的最大子段和
s1=0,lefts=0;
for(int i=center;i>=left;i--) //从中间开始往左边求最大子段和
{
lefts+=array[i];
if(lefts>s1)
{
s1=lefts;
}
}
s2=0,rights=0;
for(int i=center+1;i<=right;i++) //从中间开始往右边求最大子段和
{
rights+=array[i];
if(rights>s2)
{
s2=rights;
}
}
midsum=s1+s2; //中间的最大子段和
sum=bigger(bigger(leftsum,rightsum),midsum); //求解最大子段和
}
return sum;
}
int main()
{
int sum1=SubSegmentSum(array,0,5);
int sum2=SubSegmentSum(array2,0,8);
printf("%d\n",sum1);
printf("%d\n",sum2);
return 0;
}
4.运行结果:
5.总结:
最大子段和问题,关键是清楚连续的最大子段和的三种情况:左边、右边和左边加右边,分别求出三种情况下的最大子段和,进行比较即可以得到最大的子段和。
对左右结合情况的理解:左右结合的最大和的值S,一定是从中间位置mid为起点,往左的最大子段和S1,与从中间位置mid为起点,往右的最大子段和S2,两者之和。即S=S1+S2。