zoukankan      html  css  js  c++  java
  • 15 连续子数组的最大和

    0 引言

    题目:HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,
    常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,
    并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。
    给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

    1 举例/测试用例设置

    (1)全是非负数的情况:

     {93680124}

    全部相加即可得到最大和,也就是说全体序列就是最大和序列

    (2)全是非正数的情况:

    {-1,-13,-9,-2,-70,-4}

    某个最小的数即为最大连续子序列

    (3)既有正数又有负数以及0的情况 :

    {93,-1,-13680,-9,-2,-7124} 

      1)将序列划分为不同的子序列,划分的依据是符号是否改变(从 “+ /0” 变为 “-” 或者 从“-/0”变为 “+” ),转化为  

    {12,-1414,-187}

      2)把满足从0或者正数开始,从0或者正数结束的所有序列全部列举出来,最后找到一个最大的序列,就是要求的结果序列。

      1. 12,sum = 12

      2. 12-》-14-》14,sum = 12

      3. 12-》-14-》14-》-18-》7,sum = 1

      4. 14 ,sum = 14

      5. 14-》-18-》7, sum = 3 

      6. 7 , sum = 7 

      7. 通过比较,得出最大连续子序列的和为14

    2 具体例子抽象分析

    通过1中的分析,可是算法应当分为以下两步进行

    (1)对整个序列进行局部融合,利用额外的空间把融合序列存储起来 vector<int> myArray ,得到的序列具有以下特点:

      1. 不包含0;

      2. 奇偶相间;

    (2)以开始和结束的值 >=0 为条件,计算所有的可能序列和  

    (3)如果所有的值均小于等于0,则返回整个序列中最大的数即可

    3 demo

    写了一版非常粗糙的代码,勉强调试通过了,明天再优化一下,先粘贴出来

    int FindGreatestSumOfSubArray(vector<int> array) {
        // 判断序列是否为空
        if(array.empty())
            return 0;
        // 判断序列中是否存在正数,若不存在,则返回最小的数即可 
        int maxSum = array[0];
        for(int i=1;i<array.size();i++){
            if(array[i]>maxSum)
                maxSum = array[i];
        }
        if(maxSum <= 0)
            return maxSum;
        // 若存在正数
        // 1. 局部融合,将正数,负数和0分开融合,同时0不加入序列
        vector<int> myArray;        
        for(int i=0;i<array.size();){
            int sum = 0;
            int flag;
            if(array[i] == 0)
                flag = 0;
            else 
                flag = 1;
            int j = i;
            for(;j<array.size();j++){
                // 子序列的首个数字为0,遍历但是不加入队列              
                if(flag == 0){
                    if(array[j] == 0)
                        continue;
                    else{
                        i = j;
                        break;
                    }                        
                } 
                else{
                    if(array[j]*array[i] >= 0)
                        sum += array[j];
                    if(array[j]*array[i]<0){
                        i = j;
                        myArray.push_back(sum);
                        break;
                    }
                }                
            }
            if(j == array.size()){
                i = array.size();
                myArray.push_back(sum);
            }            
        }
        // 2. 遍历求解最大连续子序列的和        
        // 初始化maxSum
        int begin;
        for(int i=0;i<myArray.size();i++){
            if(myArray[i]>0){
                begin = i;
                maxSum = myArray[i];
                break;
            }
        }
        for(int i=begin;i<myArray.size(); i += 2){
            int subBegin = i;
            for(int j = subBegin + 2;j<myArray.size();j += 2){
                int currentSum = myArray[subBegin];
                for(int k = subBegin + 1;k<= j;k++)
                    currentSum += myArray[k];
                if(currentSum > maxSum)
                    maxSum = currentSum;
                }            
        }
        return maxSum;    
    }

    4 优化

    代码毫无疑问有很多需要改进的地方,本身思路就不是很清晰,何况写代码的能力也不太行,结构很乱,看着着实费劲。。。参考了网友的代码以及《剑指offer》,对

    算法作出如下调整。

    (1)不对序列进行融合,转而采取一次遍历的方法,将算法的复杂度从O((n/2)^2)降到O(n);

    (2)一次遍历的重点在于两个变量:当前子序列最小和 currentSum以及maxSum。当currentSum <= 0时,抛弃当前currentSum,原因在于该当前和与访问数相加得到的

    和值一定小于访问数。此时,应当将访问数的值赋值给currentSum,从该数开始重新寻找最大和。 

    (3)当currentSum > maxSum时,更新该值.

    demo如下:

    int FindGreatestSumOfSubArray(vector<int> array){
        if(array.empty())
            return 0;
        int currentSum = array[0];
        int maxSum = array[0];
        for(int i=1;i<array.size();i++){
            if(currentSum <= 0)
                currentSum = array[i];
            else
                currentSum += array[i];
            if(currentSum > maxSum)
                maxSum = currentSum;
        }
        return maxSum;
    }

    对思路进行调整之后,写完整段代码只用了可能十来分钟,相比于上个思路用了大概一个半小时,这次可真是快太多了。。。所以说写代码思路才是决定性的。

  • 相关阅读:
    【HDU1233】还是畅通工程(MST基础题)
    【HDU2122】Ice_cream’s world III(MST基础题)
    【HDU2120】Ice_cream's world I(并查集基础题)
    【HDU1232】畅通工程(并查集基础题)
    【HDU1102】Constructing Roads(MST基础题)
    【HDU1856】More is better(并查集基础题)
    【HDU1325】Is It A Tree?(并查集基础题)
    ural 1907 Coffee and Buns
    插头DP学习
    hdu 5072 Coprime
  • 原文地址:https://www.cnblogs.com/ghjnwk/p/10083736.html
Copyright © 2011-2022 走看看