连续子向量最大和问题——《编程珠玑》读书笔记

问题描述:

问题的输入是具有n个整数的向量x,输出是输入向量的任何子向量中的最大和。例如下面的向量:

31 -41 59 26 -53 58 97 -93 -23 84

那么改程序的输出就应该是59 到 97之间的总和,即187.但所有数为正数的时候,答案就是整个输入向量,可是我们必须要考虑负数的情况。

 

问题解答:

1、第一种方法式最简单的,对所有满足0 <= i <= j < n的(i,j)整数对进行迭代。找出总和最大的那个。这种方式的时间复杂度为O(n^3)。具体做法不在赘述。

2、第二种方式是将时间复杂度降低到O(n^2),需要注意到x[i..j]的总和和前面已经计算出得总和x[i..j-1]有密切关系,利用这一关系即可得到算法2:

    maxsofar = 0

    for i = [0, n)

      sum = 0

      for j = [i, n)

        sum += x[j]

        maxsofar = max(maxsofar, sum)

3、分值算法

  在本例中,初始问题的大小为n,所以将它划分为两个子问题a、b,然后递归的找出a、b中元素的最大子向量和,ma和mb。但是,我们需要考虑到,最大子向量要么整个在a中,要么整个在b中,也有可能是跨越a和b的边界。那么我们就将跨越边界的最大子向量成为mc。

  如何计算mc的值呢,mc在a中的部分是a中包含右边界的最大子向量,而mc在b中的部分则是包含左边界的最大子向量。下面是完整的C++代码:

  #include <iostream>
  using namespace std;

  #define NUM 10

  int max(int a, int b, int c = 0)
  {
      if (a < b)
      {
          a = b;
      }
      if (a < c)
      {
          return c;
      }
      return a;
  }

  int maxsum(int* ari, int begin, int end)
  {
      if (begin > end)
      {
          return 0;
      }
      if (begin == end)
      {
          return max(0, ari[begin]);
      }
      int m = (begin + end) / 2;
      int lmax = 0;
      int rmax = 0;
      int sum = 0;
      //find max crossing to left
      for (int i = m; i >= begin; i--)
      {
          sum += ari[i];
          lmax = max(lmax, sum);
      }

      sum = 0;
      //find max crossing to right
      for (int i = m + 1; i < end; i++)

      {
          sum +=ari[i];
          rmax = max(rmax, sum);
      }
      return max(lmax + rmax, maxsum(ari, begin, m), maxsum(ari, m + 1, end));
  }

  int main()
  {
      int vi[NUM] = {31, -41, 59, 26, -53, 58, 97, -93, -23, 84};
      cout << maxsum(vi, 0, 9) << endl;
      return 0;
  }

posted on 2012-05-10 15:19  aho  阅读(522)  评论(0编辑  收藏  举报

导航