日期:2019.3.9
博客期:040
星期六
这次的标题就是题目——关于求已知整数数组的连续子数组的最大和的方法,打个比方:给予数组 { 1 , -2 , 3 , -1 , 0 , 2 } ,它的连续子数组的最大和就是取得 { 3 , -1 , 0 , 2 } 时的和 4 !就是说我们需要找到元素值和最大的子数组。我们大可以考虑几种方法:
(1)先求出所有的子数组,再找出每一组的和,求出和的最大值 >>>>>>>(优化)>>>>>>>>求子数组的时候考虑负数的情况,并同时记录最大值
(2)通过假设的方法,最大值一定存在于这个数组,所以先求出最大值,之后围绕最大值向两边延申使得问题得到解决>>>>>>>>>(经验证,方法错误)>>>>>>>>> { 5 , -1 , 7 , -20 , 9 } (取得11)
(3)[个人思想方法]
我们从输入的第一个正数开始,计算它们的和记作Tnum(正数和),直到输入的数是负数,开辟一个Fnum(负数和),记录从此之后的负数和,直到输入的下一个数是正数。此时,判断 Tnum + Fnum 的大小是否大于 0 ,[注:只有负数转正数的时候判断] ,大于的话就更新Tnum的值为Tnum + Fnum + p ( p 值为你本次输入的值) ,小于或等于的话取 Tnum 为 p 。在每一次的循环中记录 Tnum 和 p 最大值 ,也就是最后的结果了。
[实例:{ -3 ,2 ,-1,0,3 ,3 ,-4,3 } ]
我们从输入的第一个数开始,-3不是正数,跳过,2是正数,我们把它记录到 Tnum 中,Tnum = 2 ,之后直到输入的是负数,所以输入的 -1 是负数 ,我们把它记录到 Fnum 中,Tnum = 2 , Fnum = -1,之后输入的是 0 ,不是正数,记录到 Fnum中,Fnum = -1 + 0 = -1,再下一个输入的是 3 ,我们知道 3 是正数,所以进行判断,因为 Tnum + Fnum = 1 > 0 , 所以我们需要把 Tnum 更新成 Tnum + Fnum + p , 这样 Tnum = 2 + ( -1 ) + 3 = 4 , (如果第二个数是 1 ,我们就要把 Tnum 设成 3 了),之后继续,输入的 3 是正数,就继续 Tnum = 4 + 3 = 7 , 下一个是 -4 , 我们把 Fnum 更新成 -4 , [ 注 : Fnum 就是单纯的记录输入的两次正数之间的负数和 ],而且下一个是 3 ,我们知道 Tnum + Fnum > 0 , 所以Tnum = 7 + ( -4 ) + 3 = 6 , 最终我们得到了 p 的最大值 3 和 Tnum 的最大值 7 , 所以我们的结果就是 7 啦!
实际结果:{ 2 , -1 , 0 , 3 , 3}
我的代码:

1 void My_Way() 2 { 3 //定义长度 4 int n; 5 //输入长度 6 cin>>n; 7 //最大值 8 int rmax = -10000; 9 //正数总值 10 int Tnum = -10000; 11 //负数总值 12 int Fnum = 0; 13 //记录是否发生转变 14 int sis = 0; 15 //标记是第几程度 16 int attitude = 0; 17 //循环 18 for(int i = 0 ; i < n ; ++i) 19 { 20 int p; 21 cin>>p; 22 if(attitude==0) //---------------------------------------[寻找第一个正数] 23 { 24 if(p<=0) 25 ; 26 else 27 { 28 Tnum = p; 29 attitude = 1; 30 } 31 } 32 else if(attitude==1) //---------------------------------------[上一个数为正数] 33 { 34 if(p<0) 35 { 36 if(sis==0) 37 { 38 sis = 1; 39 Fnum += p; 40 } 41 else 42 Fnum = p; 43 attitude = -1; 44 } 45 else 46 Tnum += p; 47 48 if(Tnum>rmax) 49 rmax = Tnum; 50 } 51 else //---------------------------------------[上一个数为负数] 52 { 53 if(p>0) 54 { 55 attitude = 1; 56 if(Tnum + Fnum > 0) 57 Tnum = (Tnum + Fnum) + p; 58 else 59 Tnum = p; 60 } 61 else 62 Fnum += p; 63 } 64 /* 65 cout<<"p="<<p<<endl; 66 cout<<"rmax="<<rmax<<endl; 67 cout<<"(p>rmax)="<<(p>rmax)<<endl; 68 */ 69 if(p>rmax) 70 rmax = p; 71 if(Tnum>rmax) 72 rmax = Tnum; 73 } 74 cout<<rmax<<endl; 75 }
我的这种方法,其实还有可以优化的地方,比如对于关系的判断啊! attitude 只能取得 -1 , 0 , 1 三个值,而我们对于它的判定有以下概率问题;
目前,我的方法空间复杂度较低(没有使用数组),时间复杂度为 O( n ) ;
1、取1个值,没有实际意义
2、取2个值:
attitude的判断值 :
排列 0 1 -1 1 -1 0 ...
<1> 1 1 查询3次 查询4次 ...
<2> 1 -1 查询3次 查询4次 ...
<3> -1 1 查询2次 查询6次 ...
<4>-1 -1 查询2次 查询6次 ...
对于attitude的判断次数的数学期望最小值为 2.5;
3、取3个值
attitude的判断值
<1> 1 1 1 查询5次
<2> 1 1 -1 查询5次
<3> 1 -1 1 查询6次
<4> 1 -1 -1 查询6次
<5> -1 1 1 查询4次
<6> -1 1 -1 查询4次
<7> -1 -1 1 查询3次
<8> -1 -1 -1 查询3次
对于attitude的判断次数的数学期望最小值为 4.5;
所以我写的应该是最优的判断顺序了!
(4) 同学的宝贵方法
取输入的第 i 个元素值为 a[i] , 还有前 i 项的和为 S[i] , 所以有:
而我们也可以使用 S[n] 来表示 a[n] ;
从而引导处真正的连续项和的公式:
我们现在可以使用 a 数组储存我们输入的数,并同时计算前几项的和记录到数组 S 中。
【初始想法】 之后只要找到 S 数组里的最大值和最小值,之后做差,不就可以得到最大值了。
<问题>你可以想想如何求数组里和的最小值?就是最小值减去最大值?当然不是!因为必须要控制 i <= j , 使得求出来的是连续和。
【改进方法】所以我们继续我们制作限定,使用 min_p 记录位于当前输入的数之前的最小的 S 数组里的值,使用 max_p 记录所有 ( p - min_p ) 中最大的值,记录到最后 max_p 就是所求的最大值了。
我根据同学的思想,制作了C++代码:
1、制作初步的实现

1 void Friend_Way() 2 { 3 //原理 p[i] + p[i+1] + p[i+2} + ... + p[j-1] + p[j] = S[j] - S[i-1] 4 5 int n; 6 //n是总数组大小 7 cin>>n; 8 //储存初始数组 9 int * q = new int [n]; 10 //储存前i项和,即 p[i] = q[0] + q[1] + q[2] + ... + q[i] 11 int * p = new int [n+1]; 12 //预处理 13 cin>>q[0]; 14 p[0] = q[0]; 15 16 //记录最大值 17 int final_max = q[0]; 18 //记录p[i]目前达到的最小值 19 int min_pos = 0; 20 21 for(int i=1;i<n;++i) 22 { 23 cin>>q[i]; 24 p[i] = q[i] + p[i-1]; 25 if(p[min_pos]>=p[i-1]) 26 { 27 min_pos = i-1; 28 } 29 if(p[i]-p[min_pos]>final_max) 30 final_max = p[i] - p[min_pos]; 31 } 32 cout<<endl; 33 cout<<"最终结果:"<<final_max<<endl; 34 }
2、节约空间,不再使用 数组 S

1 void Friend_Way_S() 2 { 3 int n; 4 cin>>n; 5 int * q = new int [n]; 6 cin>>q[0]; 7 //最终值 8 int rmax = q[0]; 9 //和值 10 int sum = q[0]; 11 //最小的值 12 int min = q[0]; 13 14 for(int i=1;i<n;++i) 15 { 16 cin>>q[i]; 17 sum += q[i]; 18 if(sum-min>rmax) 19 rmax = sum - min; 20 if(min>sum) 21 min = sum; 22 } 23 cout<<endl; 24 cout<<"最终结果:"<<rmax<<endl; 25 }
3、节约空间,不再使用 数组 a
{
int n;
cin>>n;
int p;
cin>>p;
//最终值
int rmax = p;
//和值
int sum = p;
//最小的值
int min = p;
{
cin>>p;
sum += p;
if(sum-min>rmax)
rmax = sum - min;
if(min>sum)
min = sum;
}
cout<<endl;
cout<<"最终结果:"<<rmax<<endl;
}
如果大家还有什么疑问,欢迎给我留言,大家一起交谈。