zoukankan      html  css  js  c++  java
  • 最大子段和算法分析

    最大子段和问题(Maximum Interval Sum)


    一.问题描述
      给定长度为n的整数序列,a[1...n], 求[1,n]某个子区间[i , j]使得a[i]+…+a[j]和最大.或者求出最大的这个和.例如(-2,11,-4,13,-5,2)的最大子段和为20,所求子区间为[2,4]。

    二.算法分析

      1.穷举法

     1 int start = 0;//起始位置
     2 int end = 0;  //结束位置
     3 int max = 0;
     4 for(int i = 1; i <= n; ++i)
     5 {
     6     for(int j = i; j <= n;++j)
     7     {
     8         int sum = 0;
     9         for(int k = i; k <=j; ++k)
    10             sum += a[k];
    11         if(sum > max)
    12         {
    13            start = i;
    14            end = j;
    15            max = sum;
    16         }
    17     }
    18 }

      换一种穷举思路,对于起点 i,我们遍历所有长度为1,2,…,n-i+1的子区间和,以求得和最大的一个.这样也遍历了所有的起点的不同长度的子区间,同时,对于相同起点的不同长度的子区间,可以利用前面的计算结果来计算后面的。就像传递数组参数时往往传的不是起止位置,而是起始位置和长度。

     1 int start = 0;//起始位置
     2 int end = 0;//结束位置
     3 int max = 0;
     4 for(int i = 1; i <= n; ++i)
     5 {
     6     int sum = 0;
     7     for(int j = i; j <= n;++j)
     8     {
     9         sum += a[j];
    10         if(sum > max)
    11         {
    12            start = i;
    13            end = j;
    14            max = sum;
    15         }
    16     }
    17 }

      2.分治法

        求子区间及最大和,从结构上是非常适合分治法的,因为所有子区间[start, end]只可能有以下三种可能性:

          1.在[1, n/2]这个区域内
          2.在[n/2+1, n]这个区域内
          3.起点位于[1,n/2],终点位于[n/2+1,n]内

        以上三种情形的最大者,即为所求. 前两种情形符合子问题递归特性,所以递归可以求出. 对于第三种情形,则必然包括了n/2和n/2+1两个位置,这样就可以利用第二种穷举的思路分别向左右扩张求出:

     1 int maxInterval(int *a, int left, int right)
     2  {
     3     if(right==left)
     4       return a[left]>0?a[left]:0;
     5     int center = (left+right)/2;
     6     int leftMaxInterval = maxInterval(a,left,center);
     7     int rightMaxInterval= maxInterval(a,center+1,right);
     8     int sum = 0;
     9     int left_max = 0;
    10     for(int i = center; i >= left; –i)
    11     {
    12        sum += a[i];
    13        if(sum > left_max)
    14           left_max = sum;
    15 
    16     }
    17     sum = 0;
    18     int right_max = 0;
    19     for(int i = center+1; i <= right; ++i)
    20     {
    21        sum += a[i];
    22        if(sum > right_max)
    23          right_max = sum;
    24     }
    25     int res = left_max+right_max;
    26     if(res < leftMaxInterval)
    27         res = leftMaxInterval;
    28     if(res < rightMaxInterval)
    29         res = rightMaxInterval;
    30     return res;
    31  }

      3.动态规划并扩展到二维空间

        令b[j]表示以位置 j 为终点的所有子区间中和最大的一个
        子问题:如j为终点的最大子区间包含了位置j-1,则以j-1为终点的最大子区间必然包括在其中
        如果b[j-1] >0, 那么显然b[j] = b[j-1] + a[j],用之前最大的一个加上a[j]即可,因为a[j]必须包含
        如果b[j-1]<=0,那么b[j] = a[j] ,因为既然最大,前面的负数必然不能使你更大

     1 #include<stdio.h>
     2  #include<string.h>
     3  int a[101][101],b[101],c[101];
     4  int subsequencesum(int a[],int n)
     5  {
     6      int sum=0,maxsum=-0x7fffff,i;
     7      for(i=1;i<=n;i++)
     8          if(maxsum<a[i])
     9              maxsum=a[i];
    10      if(maxsum<=0)
    11          return maxsum;
    12      memset(c,0,sizeof(c));
    13      for(i=1;i<=n;i++)
    14      {
    15             if(c[i-1]>0)
    16                 c[i] = c[i-1] + a[i];
    17             else
    18                 c[i] = a[i];
    19             if(c[i]>maxsum)
    20                 maxsum=c[i]; 
    21           /*      
    22          sum+=a[i];
    23          if(sum>maxsum)
    24              maxsum=sum;   
    25          else
    26              if(sum<0)
    27                  sum=0;
    28             */
    29      }
    30      return maxsum;
    31  }                     
    32  int main()
    33  {
    34           int n,max,ans,temp;
    35          int i,j,k,T,m;
    36           while(~scanf("%d",&n))//说的是一组,实际却是多组 
    37           {
    38              temp=ans=max=-0x7fffff;
    39              for(i=1;i<=n;i++)
    40                  for(j=1;j<=n;j++)
    41                      scanf("%d",&a[i][j]);
    42              for(i=1;i<=n;i++)
    43              {                         
    44                      memset(b,0,sizeof(b));
    45                      for(j=i;j<=n;j++)
    46                      {
    47                               for(k=1;k<=n;k++)
    48                              {
    49                                  b[k]+=a[j][k];
    50                              }
    51                              ans=subsequencesum(b,n);//算的是两列间的和, 
    52                              if(temp<ans) 
    53                                  temp=ans;
    54                      }
    55              }
    56              printf("%d\n",temp);
    57          }
    58          //while(1);
    59          return 0;
    60  }

      为加深理解,这里推荐练习一下HDU 1081,还可以扩展到三维情况,有兴趣的读者请参看:最大长方体问题。

  • 相关阅读:
    EasyRadius 动态域名DDNS设置工具,支持WayOS三代,完美解决近段时间3322和每步不稳定问题
    爱快路由计费系统easyradius隆重发布,支持V2版本,欢迎大家测试使用
    easyradius隆重发布ROS API计费接口,支持ROS 3.3以上版本,实现简单快捷的ROS宽带计费系统云端版
    easyradius通讯接口 V4全新升级,显示同步失败原因,方便用户寻找故障
    上网爱快?EasyRadius FOR 爱快V2接口测试版正式推出,欢迎广大爱迷们测试噢
    让小区运营再智能一点,EasyRadius正式向WayOs用户提供到期弹出式提示充值页面
    Easyradius对接WayOs维盟小区版XQ教程
    由于PADT伪造攻击带来的大面积掉线原因分析
    WiFidog 广告路由可修改功能更加智能化的几点看法
    TFTP 1.68智能刷机全能版发布,TTL线在CFE模式解决BCM5357如斐讯FIR302B等产品变砖问题
  • 原文地址:https://www.cnblogs.com/hxsyl/p/2948435.html
Copyright © 2011-2022 走看看