今天跟大家讨论的是最大子数组问题。
问题描述:
一个想靠着股票发家致富的股票迷提出了这么一个问题,在已经知道一个股票17天的股票价格后,股票迷就希望可以知道什么时候买入这个股票,之后在什么时候将该股票卖出才可以得到最大的利润。
问题分析:
在知道我们股票迷的需求之后,最暴力的解决方法不外乎是简单滴尝试每对可能的买进和卖出日期组合,这样自然就可以找到最适合的匹配;不过问题既然来了,我们还是本着用尽可能好的方法来解决该问题。首先,我们需要将问题变换一下,已经知道的是每天的股票价格,我们做一个减法运算,把股票价格数据变换成股票价格的日变化值,然后现在将这些数据看成是一个数组,那我们的任务就成了找到一个(注意,可能有多个最大子数组)这个数组里边的和最大子数组(也就是在数组中找到一个区间使得这个区间的股票价格增长值最大)。
我们考虑使用分之策略来求解最大子数组问题。用分治技术意味着我们先要将子数组划分成为两个规模尽量相等的子数组,也就是找到数组A[low..high]中间位置mid,那么我们要找数组A的任意一个子数组,其所处的位置必然是以下三种情况:
- 完全位于A[low..mid]
- 完全位于A[mid..high]
- 跨越了中点mid
#include<iostream>
#include<math.h>
#include<limits.h>
using namespace std;
struct sub{ //声明一个结构体变量用来存储子数组的信息
int low;
int high;
int sum; //数组和
};
sub find_Maxsubrray(int *A,int low,int high); //寻找最大子数组
sub find_Maxcrosub(int *A,int low,int mid,int high); //找到跨越中点的最大子数组
int main()
{
int A[7]={3,-1,-4,6,5,-2,7};
sub sub_result;
sub_result=find_Maxsubrray(A,0,6);
cout<<"和最大的数组是从"<<sub_result.low<<"到"<<sub_result.high<<",和是"<<sub_result.sum;
cout<<endl;
return 0;
}
sub find_Maxsubrray(int *A,int low,int high){
int mid;
sub left_sub,right_sub,cross_sub;
if(high==low)
{
cross_sub.low=low;
cross_sub.high=high;
cross_sub.sum=A[low];
return cross_sub;
}
else{
mid=(int)floor((low+high)/2.0);
left_sub=find_Maxsubrray(A,low,mid);
right_sub=find_Maxsubrray(A,mid+1,high);
cross_sub=find_Maxcrosub(A,low,mid,high);
if(left_sub.sum>=right_sub.sum&&left_sub.sum>=cross_sub.sum)
return left_sub;
else if(right_sub.sum>=left_sub.sum&&right_sub.sum>=cross_sub.sum)
return right_sub;
else
return cross_sub;
}
}
sub find_Maxcrosub(int *A,int low,int mid,int high){
int left_sum,sum,right_sum,max_left,max_right;
sub sub_max;
left_sum=INT_MIN;
sum=0;
for(int i=mid;i>=low;i--){
sum+=A[i];
if(sum>left_sum){
left_sum=sum;
max_left=i;
}
}
right_sum=INT_MIN;
sum=0;
for(int j=mid+1;j<=high;j++){
sum+=A[j];
if(sum>right_sum){
right_sum=sum;
max_right=j;
}
}
sub_max.low=max_left;
sub_max.high=max_right;
sub_max.sum=left_sum+right_sum;
return sub_max ;
}