求连续向量的最大子和问题(扫描算法)

来源:互联网 发布:360 手机网络收藏夹 编辑:程序博客网 时间:2024/05/20 06:52

问题来自《编程珠玑》这本书,我记得以前考研的时候模拟题目中也有过类似的题目,当时书上的代码特别简单易懂,不过也有些时日了,当时是怎样写的基本也就忘了。

现在回过头来在看看这个问题。


1.  问题描述

问题来自一维的模式识别,问题的输入是具有n个浮点数的向量x,输出是输入向量的任何连续子向量的最大和。例如,如果输入向量包含下面10个元素:

31

-41

59

26

-53

58

97

-93

-23

84

那么该程序的输出为x[2..6]的总和,即187。当所有数都是正数时,问题很容易解决,此时最大的子向量就是整个输入向量。当输入中含有负数时麻烦就来了:是否应该包含某个负数并期望旁边的整数会弥补它呢?

2.  简单算法

完成该任务的浅显程序就是对所有满足0<=i<=j<n的(i,j)整数对进行迭代。对每个整数对,程序都要计算x[ij]的总和,并检验总和是否大于迄今为止的最大总和。该算法的伪代码如下所示:

maxsofar = 0

for i = [0,n)

     for j = [i,n)

         sum = 0

         for k = [i,j]

              sum += x[k]

              /* sum is sum of x[i..j] */

              maxsofar = max(maxsofar, sum)

     这段代码简洁、直观并且易于理解。不幸的是,程序的运行速度也很慢。

3.  两个平方算法

有两个算法可以明显地提高第一个算法的速度,其中一个是比较明显可想的,而另外一个则不那么明显,它们的时间复杂度都是平方时间(对于输入规模n来说,需要执行O(n^2 )步)。

可以注意到,x[i..j]的总和与前面已计算出的总和(x[i..j-1]的总和)密切相关。利用这一关系即可得到算法。伪码如下:

maxsofar = 0

for i = [0,n)

     sum = 0

     for j = [i,n)

         sum += x[j]

         /* sum is sum of x[i..j] */

          maxsofar = max(maxsofar, sum)

     另一个平方算法是通过访问在外循环执行之前就已构建的数据结构的方式在内循环中计算总和。cumarr中的第i个元素包含x[0..i]中各个数的累加和,所以x[i..j]中各个数的总和可以通过计算cumarr[j]-cumarr[i-1]得到。从而得到如下所示的伪码:

cumarr[-1] = 0

for i = [0,n)

     cumarr[i] = cumarr[i-1] + x[i]

maxsofar = 0

for i = [0,n)

     for j = [i,n)

     sum = cumarr[j] - cumarr[i-1]

     /* sum is sum of x[i..j] */

     maxsofar = max(maxsofar, sum)

下面开始贴上代码  动态规划算法:

<pre name="code" class="cpp">#include<iostream>#include<conio.h>using namespace std;int findmax(int a[],int n){    int end_max=a[n-1];//记录到数组每一个元素时当前的最大和。sum用来记录全局最大的 ,从后往前 int sum=a[n-1];int i;for(i=n-2;i>=0;i--){         end_max=max(end_max+a[i],a[i]);         sum=max(sum,end_max);}return sum;} int main(){int i,end_max,sum;int a[10]={31,-41,59,26,-93,58,97,-93,-23,84};cout<<findmax(a,10)<<endl;getchar();return 0;}


0 0