问题描述
算法导论P38提到
来源:已知一股票在某一段时间内的变化趋势,问该时间段内何时买入,何时卖出,使得收益最大,求出最大值
转化:给定一个数组,从中选取一个连续的子数组,使得其元素和在所有的子数组中最大,返回该最大值。
解决方案
暴力搜索
n个元素中选取两个元素,计算两个元素之间的元素的和,将其与max相比,如果比max大,则替换;
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public static int (int []a){ int max=0; int sum=0; for(int i=0;i<a.length;i++){ for(int j=i;j<a.length;j++){ for(int k=i;k<=j;k++){ sum+=a[k]; } if(sum>max){ max=sum; } sum=0; } } return max; }
|
分治策略
把一个数组从中分开为两个子数组,最大子数组可能的情况是:在左边的子数组,在右边的子数组,含左边的一部分和右边的一部分;对于前两种情况只是将原问题分解为较小的原问题,而第三种情况既然包括左边和右边,一定含有中间的元素,因此很好解决,从左从右遍历即可;然后将这三种情况进行比较,选取最大的部分返回
这个问题和归并排序是一个道理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public static int getMaxSum2(int []a){ return findMaxSub(a,0,a.length-1); } public static 大专栏 最大子数组问题">int findMaxSub(int[] a,int left,int right){ if(left==right) return a[left]; int mid=left+(right-left)/2; int leftSum=findMaxSub(a, left, mid); int midSum=findMaxCro(a, left,mid, right); int rightSum=findMaxSub(a, mid+1, right); if(leftSum>midSum&&left>rightSum) return leftSum; if(rightSum>leftSum&&rightSum>midSum) return rightSum; return midSum; } public static int findMaxCro(int[] a,int left,int mid,int right){ int maxleft,maxright; int sum=0; maxleft=a[mid]; for(int i=mid;i>=left;i--){ sum+=a[i]; if(sum>maxleft){ maxleft=sum; } } maxright=0; sum=0; for(int i=mid+1;i<=right;i++){ sum+=a[i]; if(sum>maxright){ maxright=sum; } } return maxleft+maxright; }
|
DP解法
第sum[i-1]个元素是前i-1个元素的最大子数组的和,如果sum[i-1]小于0,则sum[i]=a[i],否则sum[i]=sum[i-1]+a[i];最后的结果result取sum[i]的最大值
sum[i]=max(a[i],sum[i-1]+a[i])
result=max(result,sum[i])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public static int getMaxSum3(int []a){ int b=0; int sum=0; for(int i=0;i<a.length;i++){ if(b>=0){ b+=a[i]; }else{ b=a[i]; } if(b>sum){ sum=b; } } return sum; }
|
参考:
http://blog.csdn.net/v_JULY_v/article/details/6444021