zoukankan      html  css  js  c++  java
  • 四种方法解决最大连续子序列和问题

    什么是最大连续子序列和问题?

    问题描述:给定一个序列(整数或浮点数),求出其中连续的子序列和最大的那一个。

    例:序列{-10 1 2 3 4 -5 -23 3 7 -21},其最大的连续子序列为{1 2 3 4}或{3 7},最大和为10.

    方法一:暴力求解

    最最普通的方法,效率十分低,一般不会用到,这里简单介绍。直接两个for循环枚举子序列的首尾,再来一个for循环计算首尾之间的序列和,计算所有的序列和,找到最大值。

    时间复杂度:O(n^3)

    效率贼低,千万不要用!(所就不贴代码了)

    方法二:预处理暴力求解

    第一种方法为什么这么慢,原因之一是每次都要计算首尾之间的序列和。基于这个考虑,我们可以对数据进行预先处理:读入数据时使用一个数组SUM[i]来记录前i项数据之和。用这种方法,只需要两个for循环枚举子序列的首尾,利用SUM数组计算子序列和,找到最大值。

    时间复杂度:O(n^2)

    还是很糟糕,建议不要用。(所以也不贴代码qwq)

    方法三:分治法求解

    把序列分成左右两部分,一般对半分,数量不等也没关系。最大子序列和的位置存在三种情况:1、完全在左半部分;2、完全在右半部分;3、跨越左右两部分。分别求出左半部分的最大子序列和、右半部分的最大子序列和、以及跨越左右两部分的最大子序列和,三者中的最大者就是要求的。

    如何求三部分的最大子序列和呢?

    左半部分的最大子序列和,可把左半部分作为新的输入序列通过该算法递归求出。右半部分的最大子序列和同理。

    接下来就是求解跨越左右两部分的最大子序列和,也就是求出包含左半部分中最右边元素的子序列的最大和,和包含右半部分中最左边元素的子序列的最大和,将两者相加即为跨越左右两个部分的最大子序列和。具体见如下代码:

    //分治算法 
    int a[999999];
    
    int MAxSubSum(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(left,center);//左半部分 
            int rightsum=MAxSubSum(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 i=center+;i<=right;i++)
            {
                rights+=a[i];
                if(rights>s2) s2=rights;
            }
            
            //取三者最大值 
            sum=s1+s2;
            if(sum<leftsum) sum=leftsum;
            if(sum<rightsum) sum=rightsum;
        }
        return sum;
    }

    时间复杂度:O(nlgn)

    运用递归思想,一层一层分解,最终解决问题。效率还好,有助于理解分治与递归,可以尝试使用。

    方法四:动态规划

    这是最常见的方法了,简单有效,好理解。

    状态转移方程:sum[i] = max{sum[i-1]+a[i],a[i]}. (sum[i]记录以a[i]为子序列末端的最大序子列连续和)

    其实完全可以不用开数组,累计sum直到sum + a < a,把sum赋值为a,更新最大值就行了。

    //动态规划算法
    int MaxSum(int n)
    {
        int sum=0,b=0;
        for(int i=0;i<n;i++)
        {
            if(b>0) b+=a[i];
            else b=a[i];
            if(b>sum) sum=b;
        }
        return sum;
    }

    时间复杂度:O(n)

    这是好用的求最大子序列的方法了,墙裂推荐!

    相关题目练习:

    hdu 1003:http://acm.hdu.edu.cn/showproblem.php?pid=1003

    hdu 1231:http://acm.hdu.edu.cn/showproblem.php?pid=1231

    作者: AlvinZH

    出处: http://www.cnblogs.com/AlvinZH/

    本人Github:https://github.com/Pacsiy/JobDu

    本文版权归作者AlvinZH和博客园所有,欢迎转载和商用,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

  • 相关阅读:
    Redis分布式锁服务(转)
    redis分布式锁(转)
    MySQL+InnoDB semi-consitent read原理及实现分析(转)
    MySQL加锁处理分析(转)
    实战经验丨PHP反序列化漏洞总结
    脚本语言丨Batch入门教程第四章:调用与传参
    福利狂欢已开启,请做好准备!
    脚本语言丨Batch入门教程第三章:逻辑判断
    WinRAR存在严重的安全漏洞影响5亿用户
    Batch入门教程丨第二章:认识变量相关概念
  • 原文地址:https://www.cnblogs.com/AlvinZH/p/6795647.html
Copyright © 2011-2022 走看看