一道算法题,看看大家的思路(续)

简介:

 “一道算法题,看看大家的思路”,看了众多的回复,本人愚钝,没有看明白其中的奥妙。在细细研究《编程之美》中的文章后,终于理解了这个算法的思路。现将这个算法的演算过程以及代码实现(VB2005)赋予其后,和各位交流。

  现再将题目复述一遍:

  题目描述:有31,-41,59,26,-53,58,97,-93,-23,84十个数。SUM(N,M)表示从第N个数到到第M个数的和。例如:SUM(2,3)=-41+59=18。问:最大的和是多少?对应的N和M是多少?

  先不管N和M的计算,直接计算SUM,看看用什么算法。

  算法一:直接遍历穷举,求出SUM。代码如下:

 

   Public  Function MaxSum1()  As  Integer
     Dim i  As  Integer, j  As  Integer, k  As  Integer, _
Sum  As  Integer, Maximum  As  Integer =  Integer.MinValue

For i = 0  To A.GetUpperBound(0)
For j = i  To A.GetUpperBound(0)
Sum = 0
For k = i  To j
Sum += A(k)
If Sum > Maximum  Then Maximum = Sum
Next
       Next
     Next
     Return Maximum
End  Function

  这个代码最容易理解,但是效率最低,算法的复杂度为O(N3)

 

  算法二,在算法一的基础上改进,因为SUM(N,M)=SUM(N,M-1)+A(M),基于这个原理,将上述代码中的最里面的一层循环去掉。

 

   Public  Function MaxSum2()  As  Integer
     Dim i  As  Integer, j  As  Integer, Sum  As  Integer, _
Maximum  As  Integer =  Integer.MinValue
For i = 0  To A.GetUpperBound(0)
Sum = 0
For j = i  To A.GetUpperBound(0)
Sum += A(j)
If Sum > Maximum  Then Maximum = Sum
Next
     Next
     Return Maximum
End  Function

 

  这个代码也很容易理解,算法执行效率有明显的提高,算法的复杂度为O(N2),但还不够。

  算法三:

  考虑数组第一个元素A(0)和最大和的一段数组A(N),……,A(M)。他们之间有如下关系:

  1、当0=N=M时,元素A(0)本身构成最大和的一段

  2、当0=N<M时,最大和的一段以A(0)开始

  3、当0<N时,A(0)和最大和一段没有什么关系。

  假设数组有N个元素,分别为A(0),A(1),……,A(N-1)

  并且我还知道从A(1)到A(N-1)的最大和为All(1),从A(1)到A(N-1)中包含A(1)的最大和为Start(1)

  那么,从A(0)到A(N-1)的最大和All(0)就一定有下面的关系式:

  All(0)=Max{A(0),A(0)+Start(1),All(1)}

  而Start(0)有如下的关系式:

  Start(0)=Max{A(0),A(0)+Start(1)}

  看了上面的分析,将N个元素的问题就转化为了N-1个元素的问题。这是我想到了递推。(书上以及很多其他的博客说是动态规划,我没有理解,个人比较愚钝)

  将上面的式子整理一下,建立好递推关系:

  Start(i)=Max{A(i),A(i)+Start(i+1)}

  All(i)=Max{Start(i),All(i+1)}

  代码如下:

 

   Public  Function Max( ByVal X  As  IntegerByVal Y  As  IntegerAs  Integer
     Return IIf(X > Y, X, Y)
End  Function

   Public  Function MaxSum3()  As  Integer
     Dim i  As  Integer, Start()  As  Integer, All()  As  Integer
     ReDim Start(A.GetUpperBound(0))
ReDim All(A.GetUpperBound(0))

Start(A.GetUpperBound(0)) = A(A.GetUpperBound(0))
All(A.GetUpperBound(0)) = A(A.GetUpperBound(0))

For i = A.GetUpperBound(0) - 1  To 0  Step -1
Start(i) = Max(A(i), A(i) + Start(i + 1))
All(i) = Max(Start(i), All(i + 1))
Next

     Return All(0)
End  Function

 

  这个算法的效率很高,算法复杂度为O(N),但是引用了两个数组。仔细观察两个数组的使用情况,发现其实只要使用两个变量就完全可以了,下面是改进的代码。

 

   Public  Function Max( ByVal X  As  IntegerByVal Y  As  IntegerAs  Integer
     Return IIf(X > Y, X, Y)
End  Function

   Public  Function MaxSum4()  As  Integer
     Dim i  As  Integer, Start  As  Integer, All  As  Integer
    Start = A(A.GetUpperBound(0))
All = A(A.GetUpperBound(0))
For i = A.GetUpperBound(0) - 1  To 0  Step -1
Start = Max(A(i), A(i) + Start)
All = Max(Start, All)
Next
     Return All
End  Function

 

 

  将上面的代码,仔细分析一下可以发现:

  Start=Max(A(i),A(i)+Start)

 

  可以改写为:

  If Start<0 Then

    Start=A(i)

  Else

    Start=A(i)+Start

  End If

  继而可以改写为

  If Start<0 Then Start=0

  Start=A(i)+Start

  而All = Max(Start, All)可以改写为

  If Start>All Then All=Start

  故上面的代码,可以改写为一个函数,减少系统的开销

 

   Public  Function MaxSum5()  As  Integer
     Dim i  As  Integer, Start  As  Integer, All  As  Integer
    Start = A(A.GetUpperBound(0))
All = A(A.GetUpperBound(0))

For i = A.GetUpperBound(0) - 1  To 0  Step -1
If Start < 0  Then Start = 0
Start += A(i)

If Start > All  Then All = Start
Next
     Return All
End  Function

 


至此,代码效率高,又简洁明了。唯一的缺憾是从数组的最后一个倒推,下面的代码改成正推

 

   Public  Function MaxSum6()  As  Integer
     Dim i  As  Integer, Start  As  Integer, All  As  Integer
    Start = A(0)
All = A(0)

For i = 1  To A.GetUpperBound(0)
If Start < 0  Then Start = 0
Start += A(i)

If Start > All  Then All = Start
Next

     Return All
End  Function

 

  以上的推导过程就是为了求出一个最大和,没有求出具体的下标。关于下标的计算,留待后文详述。

  代码格式修正于2012年1月6日


  本文转自万仓一黍博客园博客,原文链接:http://www.cnblogs.com/grenet/archive/2010/02/21/1670208.html,如需转载请自行联系原作者

相关文章
|
存储 算法 Java
【算法练习】有趣的括号匹配问题(思路+ 图解 +优化)基于java实现
1.题目描述 小洛看着一堆只包含’(‘和’)‘的括号序列犯愁了,小洛想知道这串序列里最长正确匹配的序列长度是多少,你能帮帮小洛吗?
【算法练习】有趣的括号匹配问题(思路+ 图解 +优化)基于java实现
|
机器学习/深度学习 人工智能 算法
从频度引发的c语言多重for循环乃至编写算法思路的思考
首先需要声明的是,笔者是一名有C语言基础并正在为考研而复习数据结构的大学生,本篇文章中的for循环代码来自于清华大学严蔚敏教授出版的《数据结构》。 本篇博客适用于初学者理解C语言for循环,多重for循环、数据结构频度、线性代数矩阵等知识点。 整篇文章从频度开始,讲述两个矩阵相乘算法,最后讲述整个算法的设计原理
195 4
从频度引发的c语言多重for循环乃至编写算法思路的思考
|
前端开发 容器 Docker
前端工程师学 docker 看这个就够了
虽然是做前端的,但是有时候也会做一些和前端不太相关的事儿。 最近在工作中用到了 docker,配合运维同学为自己的node项目创建了一个 docker 镜像。
134 1
前端工程师学 docker 看这个就够了
|
算法 调度
【Day31】力扣算法(超详细思路+注释)[1441. 用栈操作构建数组 ] [621. 任务调度器]
学习力扣算法(超详细思路+注释)[1441. 用栈操作构建数组 ] [621. 任务调度器]。
277 0
【Day31】力扣算法(超详细思路+注释)[1441. 用栈操作构建数组 ] [621. 任务调度器]
|
算法 索引
【Day28】力扣算法(超详细思路+注释) [1790. 仅执行一次字符串交换能否使两个字符串相等 ] [328. 奇偶链表 ][148. 排序链表]
了解(超详细思路+注释) [1790. 仅执行一次字符串交换能否使两个字符串相等 ] [328. 奇偶链表 ][148. 排序链表]。
119 0
【Day28】力扣算法(超详细思路+注释) [1790. 仅执行一次字符串交换能否使两个字符串相等 ] [328. 奇偶链表 ][148. 排序链表]
|
算法 测试技术
【Day27】 LeetCode算法刷题(思路+注释)[801. 使序列递增的最小交换次数 ]
了解算法刷题(思路+注释)[801. 使序列递增的最小交换次数 ]。
116 0
【Day27】 LeetCode算法刷题(思路+注释)[801. 使序列递增的最小交换次数 ]
|
算法
【Day24】 LeetCode算法题 (注释详细+解题思路)[43. 字符串相乘 ] [1800. 最大升序子数组和]
学习 (注释详细+解题思路)[43. 字符串相乘 ] [1800. 最大升序子数组和]。
127 0
【Day24】 LeetCode算法题 (注释详细+解题思路)[43. 字符串相乘 ] [1800. 最大升序子数组和]
|
算法
【Day19】LeetCode算法刷题(附带解题思路、代码注释详细) 【777. 在LR字符串中交换相邻字符】 【54. 螺旋矩阵】
学习了解附带解题思路、代码注释详细) 【777. 在LR字符串中交换相邻字符】 【54. 螺旋矩阵】。
115 0
【Day19】LeetCode算法刷题(附带解题思路、代码注释详细) 【777. 在LR字符串中交换相邻字符】 【54. 螺旋矩阵】
|
算法 索引
【Day15】算法刷题(解题思路+详细注释)[面试题 17.09. 第 k 个数 ][424. 替换后的最长重复字符 ][438. 找到字符串中所有字母异位词 ]
了解[面试题 17.09. 第 k 个数 ][424. 替换后的最长重复字符 ][438. 找到字符串中所有字母异位词 ]。
159 0
【Day15】算法刷题(解题思路+详细注释)[面试题 17.09. 第 k 个数 ][424. 替换后的最长重复字符 ][438. 找到字符串中所有字母异位词 ]
|
算法 C++
【牛客刷题-算法】加精 | 合并两个有序的链表 - 从思路设计、bug排除到最终实现的全过程
【牛客刷题-算法】加精 | 合并两个有序的链表 - 从思路设计、bug排除到最终实现的全过程
114 0
【牛客刷题-算法】加精 | 合并两个有序的链表 - 从思路设计、bug排除到最终实现的全过程