算法导论——最大子数组问题
2015-01-04 20:28
288 查看
方法一:
暴力求解
方法二:分治求解
方法三 线性时间实现:
方法三线性时间实现基于这样的思路:
如果a[1..j]已经得到了其最大子数组,那么a[1..j+1]最大子数组只能是两种情况
(1)a[1..j+1]的最大子数组就是a[1..j];
(2)a[1..j+1]的最大子数组是a[i..j+1],1<=i<=j;
那么,如何求得所谓的(2)中的a[i..j+1]呢?
首先需要承认这样的事实,如果一个数组a[p..r]求和得到负值,那么数组a[p..r+1]的最大子数组肯定不会是a[p..r+1],因为a[p..r]+a[r+1]<a[r+1].
在以上程序中,我们用temp存储所谓的a[p..r],只要a[p..r]的求和是负值,那么从下一个a[r+1]值开始,temp重新从零开始求和,只要temp > summax,就更新summax,这样,我们一次遍历后,就能得到最大的summax,接下来,我们证明该算法是有效的
证明:
对于所有数组元素,这样的元素对数组进行划分,如果加上该元素之前temp>0且temp+a[i]<0,那么该元素a[i]是一个边界,这样,数组会形成好多段,每段结束元素都满足temp>0且temp+a[i]<0.所以我们能得到多个划分块a[p..q],每个划分快的和是负值,划分块有这样的性质,对任意p<=i<q,显然,sum(a[p..i])>=0且sum(a[i..q])<0;
我们要证明
(1)最大子数组一定在划分块之内
证明:
根据划分快性质,容易证明,只要子数组横跨多个划分快,其求和值必定小于某个单独的划 分快中的数组求和。
(2)一定存在首元素以划分块的首元素开始的最大子数组。
证明:
对于某个划分快a[p..q],假设存在a[i..j],其中p<i<=j<q,那么根据划分块性质,a[p..i-1]+a[i..j]>=a[i..j]必定成立,得证。
所以,经历一次遍历,对于每个划分块,从首元素开始求和,得到最大值更新summax,一定能够得到最大子数组的值。
//-------------------------------------------------------------------------
求数组的最大子数组的积怎么求呢?
解决方法是保存最大负值和最大正值,实现如下:
其他思想:
另一种思路(包括最大子数组的积)
求不相交两个子数组的最大和
暴力求解
#include<stdio.h> #include<stdlib.h> #include<limits.h> int maxsubset(int *a,int len){ int summax=INT_MIN; int i,j,k; for(i=0;i<len;i++) for(j=i;j<len;j++){ int temp=0; for(k=i;k<=j;k++) temp+=a[k]; if(temp > summax) summax=temp; } return summax; } int main(){ int a[]={3,-1,2,5,-3,4,-6,-7,1,8,-3,5,9}; printf("the maxsubset:%d\n",maxsubset(a,13)); return 0; }
方法二:分治求解
#include<stdio.h> #include<stdlib.h> #include<limits.h> int cross_middle(int *a,int l,int m,int r){ int i, sum=0, l_max=INT_MIN, r_max=INT_MIN; for(i=m;i>=l;i--){ sum+=a[i]; if(sum > l_max) l_max=sum; } sum=0; for(i=m+1;i<=r;i++){//最早i=m,出现BUG sum+=a[i]; if(sum>r_max) r_max=sum; } return (l_max+r_max); } int maxsubset(int *a,int l,int r){ if(l == r) return a[l]; //if(l>r) return 0; int m=(l+r)/2, l_max=INT_MIN, r_max=INT_MIN, c_max=INT_MIN; l_max=maxsubset(a,l,m); r_max=maxsubset(a,m+1,r); c_max=cross_middle(a,l,m,r); if(l_max >= r_max&&l_max >= c_max) return l_max; else if(r_max >= l_max&&r_max >= c_max) return r_max; else return c_max; } int main(){ int a[]={3,-1,2,5,-3,4,-6,-7,1,8,-3,5,9}; printf("the maxsubset:%d\n",maxsubset(a,0,12)); return 0; }
方法三 线性时间实现:
#include<stdio.h> #include<stdlib.h> #include<limits.h> int maxsubset(int *a,int l,int r){ int i, temp=0, summax=INT_MIN; for(i=l;i<=r;i++){ temp+=a[i]; if(temp > summax) summax=temp; if(temp < 0) temp=0; } return summax; } int main(){ int a[]={3,-1,2,5,-3,4,-6,-7,1,8,-3,5,9}; printf("the maxsubset:%d\n",maxsubset(a,0,12)); return 0; }
方法三线性时间实现基于这样的思路:
如果a[1..j]已经得到了其最大子数组,那么a[1..j+1]最大子数组只能是两种情况
(1)a[1..j+1]的最大子数组就是a[1..j];
(2)a[1..j+1]的最大子数组是a[i..j+1],1<=i<=j;
那么,如何求得所谓的(2)中的a[i..j+1]呢?
首先需要承认这样的事实,如果一个数组a[p..r]求和得到负值,那么数组a[p..r+1]的最大子数组肯定不会是a[p..r+1],因为a[p..r]+a[r+1]<a[r+1].
在以上程序中,我们用temp存储所谓的a[p..r],只要a[p..r]的求和是负值,那么从下一个a[r+1]值开始,temp重新从零开始求和,只要temp > summax,就更新summax,这样,我们一次遍历后,就能得到最大的summax,接下来,我们证明该算法是有效的
证明:
对于所有数组元素,这样的元素对数组进行划分,如果加上该元素之前temp>0且temp+a[i]<0,那么该元素a[i]是一个边界,这样,数组会形成好多段,每段结束元素都满足temp>0且temp+a[i]<0.所以我们能得到多个划分块a[p..q],每个划分快的和是负值,划分块有这样的性质,对任意p<=i<q,显然,sum(a[p..i])>=0且sum(a[i..q])<0;
我们要证明
(1)最大子数组一定在划分块之内
证明:
根据划分快性质,容易证明,只要子数组横跨多个划分快,其求和值必定小于某个单独的划 分快中的数组求和。
(2)一定存在首元素以划分块的首元素开始的最大子数组。
证明:
对于某个划分快a[p..q],假设存在a[i..j],其中p<i<=j<q,那么根据划分块性质,a[p..i-1]+a[i..j]>=a[i..j]必定成立,得证。
所以,经历一次遍历,对于每个划分块,从首元素开始求和,得到最大值更新summax,一定能够得到最大子数组的值。
//-------------------------------------------------------------------------
求数组的最大子数组的积怎么求呢?
解决方法是保存最大负值和最大正值,实现如下:
#include<stdio.h> #include<stdlib.h> #include<limits.h> int sunsetmax_mul(int *a,int l,int r){ int i,temp, nmax=0,//negtive max pmax=0,//positive max _max=a[l]; nmax=(_max<0 ? _max : 0); pmax=(_max>0 ? _max : 0); for(i=l+1;i<=r;i++){ if(a[i] < 0){ temp=pmax; pmax=nmax==0 ? 0 : nmax*a[i]; nmax=temp==0 ?a[i]: temp*a[i]; } else if(a[i] > 0){ pmax=pmax==0 ? a[i]: pmax*a[i]; nmax=nmax==0 ? 0 : nmax*a[i]; } else { pmax=nmax=0; } if(pmax > _max) _max=pmax; } return _max; } int main(){ int a[]={-2,2,2,-5,2/*,5,-3,4,-6,-7,1,8,-3,5,9*/}; printf("max mul subset result is %d\n",sunsetmax_mul(a,0,4)); }
其他思想:
另一种思路(包括最大子数组的积)
求不相交两个子数组的最大和
相关文章推荐
- 《算法导论》学习笔记之最大子数组问题
- 算法导论-最大子数组问题-线性时间复杂度算法分析与实现
- 算法导论第四章-最大子数组问题
- 算法导论程序3--最大子数组问题(Python)
- 算法导论——最大子数组问题
- 第四章 最大子数组问题(股票最大收益) C++实现 算法导论
- 算法导论 最大子数组问题(分治策略)
- 算法导论例程——最大子数组问题
- 算法导论-----最大子数组问题(线性解法)
- 算法导论——分治法——最大子数组问题
- 算法导论_最大子数组问题(分治策略)
- 算法导论 4.1 最大子数组问题
- 算法导论:最大子数组问题(java实现)
- [置顶] 算法导论学习:分治策略之最大子数组问题
- 算法导论之分治策略:最大子数组问题
- 算法导论3:最大子数组问题 2016.1.3
- 算法导论学习-子数组最大和问题
- Demo003 最大连续子数组问题(《算法导论》4.1-5)
- 算法导论之最大子数组问题
- 算法导论之三最大子数组问题