zoukankan      html  css  js  c++  java
  • 最大连续子数组和算法(动态规划解释)

    之前在其他博客看到了,但是算法的关键部分完全看不懂为什么要这么做,直到最近上算法课,才终于知道到底怎么来的。

    问题描述:

      给出一个数组,求其最大连续子数组和

      例:数组{1,2,3,4,-5,10,-1,-1}的最大连续子数组和是子数组{1,2,3,4,-5,10}的和15

    算法过程:

      这个算法能从零直接想出来的人是真的厉害,我并不可以,所以我直接描述一下这个算法是怎么算的,而不描述怎么想到的了

      首先我们把原来数组记做a,然后最关键的一步,我们需要一个等长的数组b,b[j]的含义是以下一系列和中的最大值

    a[0] + a[1] + a[2] + a[3] + … + a[j]
           a[1] + a[2] + a[3] + … + a[j]
                  a[2] + a[3] + … + a[j]
                         a[3] + … + a[j]
                                ……
                                    a[j]

    那么显然可以知道的是,b数组中的最大值,就是我们想要的a的最大连续子数组和。

    接下来就是要得到数组b,初始化的话,b[0]应该为0和a[0]之中的较大者,下面探讨b[j]和b[j+1]之间的关系:

      b[j+1]只有两种取法,第一种是b[j]+a[j+1],注意b[j]本身就是最大值,所以不用再去考虑b[j]内部的情况了,第二种是a[j+1],所以

      b[j+1] = max{ b[j]+a[j+1], a[j+1] }

    OK,到此这个算法其实就算完成了,下面是直接根据这个思路打的代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int a[] = {1,2,3,4,-5,10,-1,-1};  // 原数组
        int* b = (int*)malloc(sizeof(a)); // 数组b,和原数组等长
    
        // 初始化
        b[0]= a[0] > 0 ? a[0] : 0;
    
        // 遍历构造,注意i的边界
        int length = sizeof(a) / sizeof(int); // 数组长
        for (int i = 0; i < length - 1; i++) {
            b[i+1] = b[i] + a[i+1] > a[i+1] ? b[i] + a[i+1] : a[i+1];
        }
    
        // 然后找出b中的最大值
        int max = b[0];
        for (int i = 1; i < length; i++)
            if (b[i] > max)
                max = b[i];
    
        printf("max=%i
    ", max);
        return 0;
    }
    

      然后导致我一直看不懂的地方来了,对上面这段代码进行编程上的优化,优化主要有两个地方,

      首先在上面的分析中,b[n+1]只和当前遍历到的a[n+1]和前一个b[n]直接相关,所以其实并不需要保存整个数组b,换用一个变量就可以了,其次是最大值,也是可以一遍遍历一边改的

      于是就有了目前网络上流传最多的以下这种代码:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        int a[] = {1,2,3,4,-5,10,-1,-1};  // 原数组
        int max = 0;
        int b = 0;
    
        int length = sizeof(a) / sizeof(int); // 长度
        for (int i = 0; i < length; i++) {
            b = b + a[i] > a[i] ? b + a[i] : a[i];
            max = max > b ? max : b;
        }
    
        printf("max=%i
    ", max);
        return 0;
    }

    显然这个算法是o(n)时间复杂度的,讲真最近学动态规划基本o(n^3)起步。动态规划两个基本要素,首先最优子结构性质,全局最大值基于n个子问题b的最优解,而且可以看到b[j+1]是基于b[j]给出的,也就是一个问题是基于他的子问题的最优解来解决的,其次显然子问题是有交叉的,求解b[j]需要b[j-1],递归一下就需要b[j-2],由此就出现了交叉。

    PS:LeetCode上题号53 最大子序和就是这个问题,但是貌似子数组长度不为0,所以初始化会有一点变化。

  • 相关阅读:
    简明Python3教程 12.问题解决
    简明Python3教程 11.数据结构
    【SPOJ 694】Distinct Substrings
    【codeforces Manthan, Codefest 17 C】Helga Hufflepuff's Cup
    【CF Manthan, Codefest 17 B】Marvolo Gaunt's Ring
    【CF Manthan, Codefest 17 A】Tom Riddle's Diary
    【SPOJ 220】 PHRASES
    【POJ 3261】Milk Patterns
    【POJ 3294】Life Forms
    【POJ 1226】Substrings
  • 原文地址:https://www.cnblogs.com/guobaoxu/p/11749943.html
Copyright © 2011-2022 走看看