最大子数组问题(分治法)--[算法导论]

2014-11-24 03:31:34 · 作者: · 浏览: 0

这题的思想是书上的(《算法导论》),代码当然也是按照书上伪码写出的;

之前已用动态规划解决这个问题,所以问题也不用多说,简述如下:

《算法导论》中引入这个问题是通过股票的购买与出售,经过问题转换,将前一天的当天的股票差价重新表示出来,即转为了一个最大子数组的问题,具体内容我不多说,转的内容是:

13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7

找到这连续的16个数里面的连续和最大的子数组;

说一下书中的思想吧(语言组织是书中的,自认总结不会比书上好):

假定我们要寻找子数组A[low..high]的最大子数组,使用分治法意味着我们要将子数组划分为两个规模尽可能相等的子数组。也就是说,找到子数组的中央位置,比如mid,然后求解两个子数组A[low..mid]和A[mid + 1..high]。所以,A[low..high]的任何连续子数组A[i..j]所处的位置必然是三种情况之一:

1.完全位于子数组A[low..mid]中, 因此low<=i<=j<=mid;

2.完全位于子数组A[mid + 1..high]中,因此mid<=i<=j<=high;

3.跨越了中点,因此low<=i<=mid

因此,A[low..high]的一个最大子数组所处的位置必然是这三种情况之一。实际上,A[low..high]的一个最大子数组必然是完全位于A[low..mid]中、完全位于A[mid + 1..high]中或者跨越中点的所有子数组中和最大者。

代码:

#include 
   
     const int Infinite = -10000; int max_left = 0; int max_right = 0; using namespace std; int FindMaxCrossSubarray(int A[], int low, int mid, int high) //跨越 { int left_sum = Infinite; int sum = 0; for (int i = mid; i >= low; i--) //左半部的最大子数组 { sum += A[i]; if (sum >left_sum) { left_sum = sum; max_left = i; } } int right_sum = Infinite; sum = 0; for (int i = mid + 1; i <= high; i++) //右半部的最大子数组 { sum += A[i]; if (sum > right_sum) { right_sum = sum; max_right = i; } } return left_sum + right_sum; } int FindMaxSubarray(int A[], int low, int high) { int left_sum, right_sum, cross_sum; if (high == low) //一个元素 { return A[low]; } else { int mid = (low + high) / 2; //分治 left_sum = FindMaxSubarray(A, low, mid); //前半部 right_sum = FindMaxSubarray(A, mid + 1, high); //后半部 cross_sum = FindMaxCrossSubarray(A, low, mid, high); //跨越前后 if (left_sum >= right_sum && left_sum >= cross_sum) //最大子数组在左边 return left_sum; else if (right_sum >= left_sum && right_sum >= cross_sum) //右边 return right_sum; else //跨越 return cross_sum; } } int main() { int a[] = {13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7}; //int a[] = {23, 4}; int length = sizeof(a) / sizeof(int); cout<
    
     

\

欢迎指点,o(∩_∩)o