最大子段和问题

给定的n个整数(可以是负数)的序列啊a[1..n],求连续的的子段中和最大的值,并给出子段的启始下表和结束下标。

注意:子段中可能包含负数,但是必须是连续的,中间不可断开

若不允许负数,则只要计算连续正数中和最大的,复杂性较原来低

若可以见断,则问题只要找出数组中全部是正数的数之和,问题更加简单

 

原问题的求解方法

一、穷举遍历法

该方法直接列举n个数种不同的子段个数,并求和,故有n*n种,时间复杂性为O(n*n)

需要注意的是,在求字段和的过程貌似需要再次循环,但是通过技巧,可以省略,在穷举子段的同时求和并比较大小(找最大的,编程的基本功)

代码如下:

int MaxSum(int n,int *a,int &besti,int &bestj)
{
 int sum=0;
 for(int i=0;i<n;i++)
  for(int j=i;j<n;j++)
  {
   int subsum=0;
   for(int k=i;k<=j;k++)
    subsum+=a[k];
   if(sum<subsum)
   {
    sum=subsum;
    besti=i;
    bestj=j;
   }
  }
  return sum;
}

//循环穷举改写
int MaxSum(int n,int *a,int besti,int bestj)
{
 int sum=0;
 for(int i=0;i<n;i++)
 {
  int subsum=0;
  for(int j=i;j<n;j++)
  {
   subsum+=a[j];
   if(sum<subsum)
   {
    sum=subsum;
    besti=i;
    bestj=j;
   }
  }

 }
}

 

 

方法二:分治法

该方法的关键是找到分治的中间点,以及如何将分治后的结果比较拼接为最终答案

取中间数据,递归求出左右的最大值,然后将在左右两边的,即包含中间节点的最大值求出,分别与左右的递归值比较,其中最大的为结果。

注意:递归的出口1,左右相等,并排除掉负数,即含负数的值为0‘

            递归的出口2,左右不等,在得到左右相等的结果后,比较左右两边的包含中间的最大值,必须做一个加法操作,并做判断(排除掉中间值比较小的部分),其中,如果有负值,则取0;

递归的过程是不断往下求解,最终通过最最简单的问题的答案网上走,得出原问题的答案。递归树是其中的关键以及程序的调用形式

int MaxSubSum(int *a,int left,int right)
{
 int sum=0;
 if(left==right)
  sum=a[left]>0?a[left]:0;
 else
 {
  int center=(left+right)/2;
  int leftsum=MaxSubSum(a,left,center);
  int rightsum=MaxSubSum(a,center+1,right);
  int s1=0;
  int lefts=0;
  for(int i=center;i>=left;i--)
  {
   lefts+=a[i];
   if(lefts>s1)
    s1=lefts;
  }
  int s2=0;
  int rights=0;
  for(int j=center+1;j<=right;j++)
  {
   rights+=a[j];
   if(rights>s2)
    s2=rights;
  }
  sum=s1+s2;
  if(sum<leftsum)sum=leftsum;
  if(sum<rightsum)sum=rightsum;
 }
 return sum;
}

int MaxSum(int n,int *a)
{
 return MaxSubSum(a,0,n-1);
}

 

方法三、动态规划方法

该方法的本质是通过对变量数据结构的定义来表示求解问题中重复部分,也就是通过数学上的规律关系,简化问题的计算,注意,只是简化了问题的求解方法。简单的关系式,甚至一个变量,但是包含的是数学上的规律,简单即复杂。

最大子段和问题设置数组b[j]:表示以b[j]结尾的必须包含b[j]的子段中和最大,求出所有的b[j]后,找出b[1...n]中最大的值极为最大子段和。

设置b[j]的原因是b[j]很好求,如果b[j-1]<0;则b[j]=a[j];否则b[j]=b[j-1]+a[j];

b[1..n]中肯定包含a[1..n]的最大子段和,因为最大字段和是其中的一个特例,极最大值。

这些主要是一个数学求和公式找出求和中间的最大值的数学公式化简而来。

程序中给出了最大子段和的启始位置和结束位置,因为只要最大的值,为了简化存储空间,中间的计算结果全部平抛弃,只保留最大值,所以该方法空间和时间都达到了巨大的优化,这也是数学的魅力所在

 

 

int MaxSum(int n,int *a,int &bestj,int &besti)
{
 int sum=0,b=0;
 for(int i=0;i<n;i++)
 {
  if(b>0)
   b+=a[i];
  else
   b=a[i];
  if(sum<b)
  {
   sum=b;
   besti=i;
  }
 }
 
 if(a[besti]==sum)
 {
  bestj=besti;
 }
 else
 {
  int sum1=sum;
  for(int j=besti;sum1>0;j--)
  {
   sum1-=a[j];
  }
  bestj=j+1;
 }
 return sum;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值