434
社区成员
发帖
与我相关
我的任务
分享给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时,定义子段和为0。
输入格式:
输入有两行:
第一行是n值(1<=n<=10000);
第二行是n个整数。
输出格式:
输出最大子段和。
输入样例:
在这里给出一组输入。例如:
6
-2 11 -4 13 -5 -2
输出样例:
在这里给出相应的输出。例如:
20
利用分治法来解决,题目是要求在数组中寻找最大字段和,(用递归来写)如果只有一个元素(n=0),只需要判断该元素是否大于零,如果大于零的话就返回该元素,如果不是的话就返回零;如果元素个数多于一个的话(n>1),就采用二分,将数组一分为二,在左右两段数组寻找最大字段和,那么左数组和右数组就分别获得一个最大字段和。还有一种情况,就是最大字段和既不在数组左边,也不在数组右边,而是在数组中间。那就需要从数组中间的元素向两边求出这个最大字段和。由上就求到了三个最大字段和,再较大小左、中、右的最大字段和的大小返回最大的一个字段和即可。
代码如下:
#include <iostream>
using namespace std;
int a[10000];
int n;
int max(int l,int r)
{
int maxL,maxR,maxM;//左数组的最大字段和、左数组的最大字段和、中间的最大字段和
if(r==l)//如果只有一个元素,大于0返回该元素,小于0返回0
{
if(a[l]>=0)
return a[l];
else
return 0;
}
int mid=(r+l)/2;
maxL=max(l,mid);//左数组求最大子段和
maxR=max(mid+1,r);//右数组求最大字段和
int flagL=0,flagR=0,m_maxL=0,m_maxR=0;//求中间的最大字段和
for(int i=mid;i>=l;i--)
{
flagL+=a[i];
if(flagL>m_maxL)
m_maxL=flagL;
}
for(int i=mid+1;i<=r;i++)
{
flagR+=a[i];
if(flagR>m_maxR)
m_maxR=flagR;
}
maxM=m_maxR+m_maxL;
if(maxL>maxR && maxL>maxM)
return maxL;
else if(maxR>maxL && maxR>maxM)
return maxR;
else if(maxM>maxL && maxM>maxR)
return maxM;
}
int main()
{
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
cout<<max(0,n-1)<<endl;
return 0;
}
时间复杂度:划分的时间复杂度是O(1),求解子问题复杂度为2T(n/2),求解中间最大字段和的是O(n),根据主定理,所以时间复杂度是O(nlog n)。
分治法是将一个难以解决的大问题分割成一些规模较小的相同问题,以便各个击破,分而治之。反复用分治手段,可以使子问题与原问题一致但是规模却不断变小,最终子问题缩小到很容易解决。我学了分治法后,最直接的体会就是写的代码简洁易懂,通过递归调用自身,减少规模,使子问题比较容易解决。比如本题求最大字段和,分派问题,快速排序,合并排序等等。