zoukankan      html  css  js  c++  java
  • 最大子数组问题,分治法求解

      最近学习《算法导论》,觉得有些东西真的是很奇妙,分治法竟然如此厉害。

      问题描述:给定一个数组,长度为n,数组中有正数有负数,现在要求找到连续的m个元素组成的子数组,使得这些元素之和是所有子数组中最大的。

    乍一看,一般人的解法就会是暴力解法,即从n个数种任意取出两个数,一个作为开头,另一个为结尾,这样花费的时间为O(n2)。但是如果我们使用分治法的思想来考虑问题就会发现,可以用递归来做。我们把数组分成两部分,mid=(low+high)/2;那么这个最大子数组要么在左半个数组内,要么在右半个数组内,要么是一半在左边一半在右边。这样我们可以使用递归的思想,每次把数组分两部分,分别讨论三种情况,那么解决问题的效率就能提高到nlogn的水平。

      下面是我根据原书算法,写的C程序。

    #include <stdio.h>
    
    #define MININUM -1000000
    typedef struct node{
        int left;
        int right;
        double sum;
    }Result;
    
    Result findMaxCrossingSub(double *A,int low,int mid,int high)
    {
        double left_sum = MININUM;
        double right_sum= MININUM;
        double sum = 0;
        int max_left;
        int max_right;
        Result result;
    
        for(int i = mid;i>=low;i--)
        {
            sum = sum + A[i];
            if(sum > left_sum)
            {
    
                left_sum = sum;
                max_left = i;
            }
        }
        sum = 0;
    
        for(int j = mid+1 ;j<=high;j++)
        {
            sum = sum + A[j];
            if(sum > right_sum)
            {
                right_sum=sum;
                max_right = j;
            }
        }
        result.left = max_left;
        result.right= max_right;
        result.sum  = left_sum + right_sum;
    
        return result;
    }
    
    Result findMaxSubArray(double *A,int low,int high)
    {
        Result result_left,result_right,result_cross;
        if(low == high)
        {
            result_left.left = low;
            result_left.right= high;
            result_left.sum  = A[low];
            return result_left;
        }
        else
        {
            int mid = (low+high)/2;
            result_left = findMaxSubArray(A,low,mid);
    
            result_right= findMaxSubArray(A,mid+1,high);
            
            result_cross= findMaxCrossingSub(A,low,mid,high);
    
            if(result_left.sum >= result_right.sum && result_left.sum >= result_cross.sum)
                return result_left;
            else if(result_right.sum >= result_left.sum && result_right.sum >= result_cross.sum)
                return result_right;
            else
                return result_cross;
        }
    }
    
    int main()
    {
        Result result;
        double A[16]={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
        
        result = findMaxSubArray(A,0,15);
    
        printf("最大数组左边界:%d\n右边界:%d\n最大子数组和:%f\n",result.left+1,result.right+1,result.sum);
    
        return 0;
    }


    思考:这个问题只是一个基本的模型,在实际生活中有很多利用到它的例子,比如根据股票的跌涨,预测什么时候买进,什么时候抛出。最主要是它教会了我们一个解决问题的方法,以后但凡O(n2)的问题,我们都可以思考一下,我们是否可以做得更好。

  • 相关阅读:
    「BZOJ 1297」「SCOI 2009」迷路「矩阵乘法」
    「BZOJ 1831」「AHOI 2008」逆序对「贪心」
    「BZOJ 1791」「IOI 2008」Island「基环树」
    WC2019 冬眠记
    「ZOJ 1354」Extended Lights Out「高斯消元」
    「BZOJ 3270」博物馆「高斯消元」
    「学习笔记」泰勒级数
    获取iPhone的UDID
    面试题
    Java的post(HTTPS)请求-----接口测试
  • 原文地址:https://www.cnblogs.com/crossing/p/2951508.html
Copyright © 2011-2022 走看看