• 最大子数组问题


    问题分析:在一个长为n的数组A[1..n]中,找到这样的i、j,使A[i...j]中元素的和最大,称A[i...j]为最大子数组。

    算法一(分治思想):

    数组A[low...high]的最大子数组必然满足三种情况:

    1)子数组在A[low...mid]中;

    2)子数组在A[mid+1,high]中;

    3)子数组跨越中点mid。

    因此可以使用递归分治方法计算find_max(A, low,  high),其中满足:

    find_max(A, low, high) = max(find_max(A, low, mid), find_max(A, mid+1, high), cross_max(A, low, mid,  high))

    cross_max(A, low, mid, high)是为了找到子数组跨越中点时的最大情况。

    使用c++实现,代码如下:

    #include<iostream>
    using namespace std;
    #define len 10
    //定义输出信息数据结构,输出子数组左右下标,以及元素和
    typedef struct info{
          int low;
          int high;
          int sum;
    }*Info, Info1;
    //创建输出结构info
    Info createinfo(int A[], int low, int high){
          Info info = new Info1;
          int sum = 0;
          for (int i=low;i<=high;i++)
              sum += A[i];
          info->low = low;
          info->high = high;
          info->sum = sum;
          return info;
    }
    //子数组跨越中点时的最大情况
    Info find_cross_max(int A[], int low, int mid, int high){
          int left_sum = A[mid];
          int right_sum = A[mid+1];
          int sum = 0;
          int left=mid;
          int right=mid+1;
          for(int i=mid;i>=low;i--){
                sum += A[i];
                if(sum > left_sum){   //这里不用>=,得到的结果是长度最短的最大子数组;使用>=得到的是最长的最大子数组
                    left_sum = sum;
                    left = i;
                }
          }
          sum = 0;
          for(int i=mid+1;i<=high;i++){
                sum += A[i];
                if(sum > right_sum){
                    right_sum = sum;
                    right = i;
                }
          }
          return createinfo(A, left, right);
    }
    //递归调用该函数,用分治法寻找最大子数组
    Info find_maximum_subarray(int A[], int low, int high){
      if(high == low)
                return createinfo(A, low, high);
          else{
                int mid = (low + high) / 2;
                Info left_info = find_maximum_subarray(A, low, mid);
                Info right_info = find_maximum_subarray(A, mid+1, high);
                Info cross_info = find_cross_max(A, low, mid, high);
                if (left_info->sum >= right_info->sum && left_info->sum >= cross_info->sum)
                      return left_info;
                else if (right_info->sum >= left_info->sum && right_info->sum >= cross_info->sum)
                      return right_info;
                else
                      return cross_info;
          }
    }
    int main(){
          int A[len] = {2,5,1,8,-5,8,0,0,-9,8};
          Info max_subarray = find_maximum_subarray(A, 0, 9);
      out<<"左下标为:"<<max_subarray->low<<endl<<"右下标为:"<<max_subarray->high<<endl<<"子数组大小为:"<<max_subarray->sum<<endl;
          return 0;
    }

    算法二(线性复杂度):

    对于数组A[1...n],从左到右遍历数组,记录目前为止处理过的最大子数组,直到遍历完整个数组,具体思路如下:

    对于1<=j<=n,记录A[1...j]的最大子数组和最大A[i...j]的值,那么A[1...j+1]的最大子数组为下面3种情况之一,取最优即可:

    1)A[1...j]的最大子数组

    2)A[i...j]+A[j+1]

    3)A[j+1]

    具体c++程序实现:

    #include<iostream>
    #define len 10
    using namespace std;
    
    void find_max_array(int A[],int low,int high){
        int left1=0,right1=0,sum1=A[low];
        int left2=0,right2=0,sum2=A[low];
        for(int i=low+1;i<=high;i++){
            if(sum2<=0){
                left2 = i;
                right2 = i;
                sum2 = A[i];
            }
            else{
                right2 = i;
                sum2 = sum2+A[i];
            }
            if(sum1<sum2){
                left1 = left2;
                right1 = right2;
                sum1 = sum2;
            }
        }
        cout<<"左下标为:"<<left1<<endl;
        cout<<"右下标为:"<<right1<<endl;
        cout<<"子数组和为:"<<sum1<<endl;
    }
    
    int main(){
        int A[len] = {2,5,1,8,-5,8,0,0,-9,8};
        find_max_array(A,0,len-1);
        return 0;
    }

    总结:分治方法可能会得到算法复杂度低于暴力求解的算法,对于某些问题,分治策略虽然能给出较优算法,但不使用该策略甚至可以能做得更好,该问题就是很好的例子。

  • 相关阅读:
    删除使用RMAN命令备份的文件
    查看.Net Framework版本的方法(zz)
    c# Foreach last (zz)
    Visual Studio 2008 QFE (zz)
    打印机的接口
    牛人的PENTAX单反之路
    我新进的宾得K10D机器和镜头
    买车险要有足额第三者责任险
    谈谈P家的SUPERTAKUAMR
    硬盘接口总结
  • 原文地址:https://www.cnblogs.com/zz-zhang/p/11379050.html
Copyright © 2020-2023  润新知