题目要求:
随机给出一个整数序列,用分治法选出其中连续且非空的一段使得这段和最大
主要思路:
先随机生成一组有正负的数组(题目中有为空这个要求,暂时没有找到生成有正负又有空的数组应该怎样生成,后面有时间在看看)
分治法需要将数组从中间分开,那么最大子数组就会有3种情况,要么完全在左半边数组,要么完全在右半边数组,要么跨立在分界点上。最大子数组完全在左数组、右数组采用递归方法解决。int sum1=calc(s,mid,l1,r1);int sum2=calc(mid+1,e,l2,r2);最大连续子数组跨立在分界点上:实际上是左数组的最大后缀和右数组的最大前缀的和。因此,从分界点向前扫,向后扫然后把找到的左边最大后缀和右边最大前缀加起来,得到的值与前两种情况找到的值对比,即可找出该数组的最大连续子数组。
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <map>
#include <ctime>
#include <cmath>
#define N 1000005
using namespace std;
int a[1005];
int calc(int s,int e,int &l,int &r)
{
int l1,l2,l3,r1,r2,r3;
///if(s==e) return a[s]>0?a[s]:0;
if(s==e)
{
if(a[s]>0)
{
l=s;
r=s;
return a[s];
}
else return 0;
}
int mid=(s+e)>>1;
//cout<<"中间数为"<<mid;
int sum1=calc(s,mid,l1,r1); //递归找出在左边的最大连续子数组
int sum2=calc(mid+1,e,l2,r2); //递归找出右边的最大连续子数组
int sl=0,sr=0,t=0;
for(int i=mid; i>=s; i--) //从分界点向前扫,找出左边的最大后缀
{
t+=a[i];
if(sl<t) sl=t,l3=i;
}
t=0;
for(int j=mid+1; j<=e; j++) //从分界点向后扫,找出右边的最大后缀
{
t+=a[j];
if(sr<t) sr=t,r=j,r3=j;
}
int ss=sl+sr; //找出跨在分界线上的最大连续子数组
l=l3,r=r3;
if(ss<=sum1) ss=sum1,l=l1,r=r1; //比较三种情况下最大的连续子数组
if(ss<=sum2) ss=sum2,l=l2,r=r2;
return ss;
}
int main()
{
int n;
printf("输入数列长度:");
cin>>n;
cout<<"随机生成的数组为:";
srand(unsigned(time(NULL))); //设置随机种子
for(int i=0;i<n;i++)
{
a[i]=(rand()%200)/10-10; //调整rand(),使得横纵坐标范围为[-10,10]
cout<<a[i]<<" ";
}
int l=1,r=1;
int sum=calc(1,n,l,r);
if(sum>0)
{
cout<<"最大子段和:"<<sum<<endl;
cout<<"起点是:第"<<l+1<<"个数"<<a[l]<<","<<"终点是:第"<<r+1<<"个数"<<a[r]<<endl;
}
else
{
cout<<"最大子段和:"<<sum<<endl;
cout<<"无起点和终点!"<<endl;
}
cout<<endl;
return 0;
}