编程之美2.14扩展问题1 求子数组和的最大值(首尾可以相连)

整体思路:

问题的解可以分为两种情况:
1)解没有跨过A[n-1]到A[0],即普通的求子数组和的最大值
2)解跨过A[n-1]到A[0]
对第二种情况,只要找到从A[0]开始和最大的一段(A[0],...,A[j])(0<=j<n)以及A[n-1]结尾的和最大的一段(A[i],...,A[n-1])(0<=i<n),那么第2种情况中,和的最大值M_2为:
M_2=A[i]+...A[n-1]+A[0]+...+A[j]
如果i<=j,则
M_2=A[0]+...+A[n-1] 子数组和为负的最小值(数组元素全为正则返回0)
否则
M_2=A[0]+...+A[j]+A[i]+...+A[n-1]

 

代码如下,其中也分了A[0]+...+A[n-1]全为负的时候返回0(代码中define RETURN_ZERP)跟返回最大的负数(RETURN_MAXMINUS)这两种情况,lpos对应于整体思路中的i,rpos对应于整体思路中的j

[cpp]  view plain copy
  1. /*******************************问题描述**************************** 
  2. 一个有N个整数元素的一维数组(A[0],A[1],...A(n-1)(首尾可以相邻),它包含 
  3. 很多子数组,求子数组之和的最大值,当数组元素全部为负的时候,有两种处 
  4. 理办法,第一种是返回0,第二种是返回数组中最大的负数 
  5. *******************************************************************/  
  6.   
  7. /*******************************整体思路*************************** 
  8. 问题的解可以分为两种情况: 
  9. 1)解没有跨过A[n-1]到A[0],即普通的求子数组和的最大值 
  10. 2)解跨过A[n-1]到A[0] 
  11. 对第二种情况,只要找到从A[0]开始和最大的一段(A[0],...,A[j])(0<=j<n) 
  12. 以及A[n-1]结尾的和最大的一段(A[i],...,A[n-1])(0<=i<n),那么第2种情况 
  13. 中,和的最大值M_2为: 
  14. M_2=A[i]+...A[n-1]+A[0]+...+A[j] 
  15. 如果i<=j,则 
  16. M_2=A[0]+...+A[n-1]-子数组和为负的最小值 
  17. 否则 
  18. M_2=A[0]+...+A[j]+A[i]+...+A[n-1] 
  19. *******************************************************************/  
  20.   
  21. #include<iostream>  
  22. using namespace std;  
  23.   
  24. #define RETURN_MAXMINUS  
  25. #ifdef RETURN_MAXMINUS  
  26. /*********************************动态规划************************************** 
  27. 假设A[0],A[1],...A(n-1)的最大子段为A[i],...,A[j],则有以下3种情况, 
  28. 1)当0=i=j的时候,元素A[0]本身构成和最大的一段 
  29. 2)当0=i<j的时候,和最大的一段以A[0]开头 
  30. 3)当0<i时候,元素A[0]跟和最大的一段没有关系 
  31. 则原始问题A[0],A[1],...A(n-1)的解All[0]=max{A[0],A[0]+Start[1],ALL[1]} 
  32. 求得A[0],A[1],...A[n-1](首尾不连接)的情况后再考虑整体思路中的第二种情况 
  33. *********************************************************************************/  
  34. //从尾到首动态规划  
  35. int MaxSum(int *A,int length){  
  36.     //先求出A[0],A[1],...A[n-1](首尾不连接)的情况下子数组和最大值nAll  
  37.     int nStart=A[length-1];  
  38.     int nAll=A[length-1];  
  39.     for(int i=length-2;i>=0;i--){  
  40.         nStart=max(A[i],A[i]+nStart);  
  41.         nAll=max(nStart,nAll);  
  42.     }  
  43.     //下面处理整体思路的第二种情况,即跨过A[n-1],A[0]  
  44.     //先求A[n-1]结尾的和最大的一段(A[i],...,A[n-1])(0<=i<n)  
  45.     int sum=0;  
  46.     int ltempmax=-10000000;  
  47.     int lpos=length;  
  48.     for(int i=length-1;i>=0;i--){  
  49.         sum+=A[i];  
  50.         if(sum>ltempmax){  
  51.             ltempmax=sum;  
  52.             lpos=i;  
  53.         }  
  54.     }  
  55.   
  56.     //求A[0]开始和最大的一段(A[0],...,A[j])(0<=j<n)  
  57.     sum=0;  
  58.     int rtempmax=-10000000;  
  59.     int rpos=-1;  
  60.     for(int i=0;i<length;i++){  
  61.         sum+=A[i];  
  62.         if(sum>rtempmax){  
  63.             rtempmax=sum;  
  64.             rpos=i;  
  65.         }  
  66.     }  
  67.   
  68.     //如果lpos<=rpos,则循环数组中可能出现的子数组最大值要么是A[0]...A[n-1]子数组和的最大值nAll  
  69.     //要么是整个数组A[0]...A[n-1]的和再减去A[0]...A[n-1]中子数组和为负数的最小值  
  70.     if(lpos<=rpos){  
  71.         //求数组中和为负数且的最小值  
  72.         int minStart=0;  
  73.         int minAll=0;  
  74.         for(int i=0;i<length;i++){  
  75.             minStart=min(0,A[i]+minStart);  
  76.             minAll=min(minStart,minAll);  
  77.         }  
  78.         int tempmax=ltempmax+rtempmax;  
  79.         for(int i=lpos;i<=rpos;i++){  
  80.             tempmax-=A[i];  
  81.         }  
  82.         //比较A[0]...A[n-1]子数组和的最大值nAll跟A[0]...A[n-1]的和再减去A[0]...A[n-1]中子数组和为负数的最小值  
  83.         return max(nAll,tempmax-minAll);  
  84.     }else{  
  85.         //比较A[0]+...+A[j]+A[i]+...+A[n-1]即ltempmax+rtempmax的值跟A[0]...A[n-1]子数组和的最大值nAll  
  86.         return max(nAll,ltempmax+rtempmax);  
  87.     }  
  88.   
  89. }  
  90. #endif  
  91.   
  92. //#define RETURN_ZERO  
  93. #ifdef RETURN_ZERO  
  94. /*********************************动态规划************************************** 
  95. 假设A[0],A[1],...A(n-1)的最大子段为A[i],...,A[j],则有以下3种情况, 
  96. 1)当0=i=j的时候,元素A[0]本身构成和最大的一段 
  97. 2)当0=i<j的时候,和最大的一段以A[0]开头 
  98. 3)当0<i时候,元素A[0]跟和最大的一段没有关系 
  99. 则原始问题A[0],A[1],...A(n-1)的解All[0]=max{A[0],A[0]+Start[1],ALL[1]} 
  100. 求得A[0],A[1],...A[n-1](首尾不连接)的情况后再考虑整体思路中的第二种情况 
  101. *********************************************************************************/  
  102. //从尾到首动态规划  
  103. int MaxSum(int *A,int length){  
  104.     //先求出A[0],A[1],...A[n-1](首尾不连接)的情况下子数组和最大值nAll  
  105.     int nStart=0;  
  106.     int nAll=0;  
  107.     for(int i=length-1;i>=0;i--){  
  108.         nStart=max(0,A[i]+nStart);  
  109.         nAll=max(nStart,nAll);  
  110.     }  
  111.     //下面处理整体思路的第二种情况,即跨过A[n-1],A[0]  
  112.     //先求A[n-1]结尾的和最大的一段(A[i],...,A[n-1])(0<=i<n)  
  113.     int sum=0;  
  114.     int ltempmax=-10000000;  
  115.     int lpos=length;  
  116.     for(int i=length-1;i>=0;i--){  
  117.         sum+=A[i];  
  118.         if(sum>ltempmax){  
  119.             ltempmax=sum;  
  120.             lpos=i;  
  121.         }  
  122.     }  
  123.   
  124.     //求A[0]开始和最大的一段(A[0],...,A[j])(0<=j<n)  
  125.     sum=0;  
  126.     int rtempmax=-10000000;  
  127.     int rpos=-1;  
  128.     for(int i=0;i<length;i++){  
  129.         sum+=A[i];  
  130.         if(sum>rtempmax){  
  131.             rtempmax=sum;  
  132.             rpos=i;  
  133.         }  
  134.     }  
  135.   
  136.     //如果lpos<=rpos,则循环数组中可能出现的子数组最大值要么是A[0]...A[n-1]子数组和的最大值nAll  
  137.     //要么是整个数组A[0]...A[n-1]的和再减去A[0]...A[n-1]中子数组和为负数的最小值  
  138.     if(lpos<=rpos){  
  139.         //求数组中和为负数且的最小值  
  140.         int minStart=0;  
  141.         int minAll=0;  
  142.         for(int i=0;i<length;i++){  
  143.             minStart=min(0,A[i]+minStart);  
  144.             minAll=min(minStart,minAll);  
  145.         }  
  146.         int tempmax=ltempmax+rtempmax;  
  147.         for(int i=lpos;i<=rpos;i++){  
  148.             tempmax-=A[i];  
  149.         }  
  150.         //比较A[0]...A[n-1]子数组和的最大值nAll跟A[0]...A[n-1]的和再减去A[0]...A[n-1]中子数组和为负数的最小值  
  151.         return max(nAll,tempmax-minAll);  
  152.     }else{  
  153.         //比较A[0]+...+A[j]+A[i]+...+A[n-1]即ltempmax+rtempmax的值跟A[0]...A[n-1]子数组和的最大值nAll  
  154.         return max(nAll,ltempmax+rtempmax);  
  155.     }  
  156.   
  157. }  
  158. #endif  
  159.   
  160. #define MAIN  
  161. int main(){  
  162.     int a[6]={1,-2,3,5,-3,2};  
  163.     int b[6]={1,-2,3,5,-1,2};  
  164.     int c[6]={2,-1,3,5,-2,1};  
  165.     int d[6]={-9,-2,-3,-5,-3};  
  166.     int e[3]={3,-2,3};  
  167. #ifdef RETURN_MAXMINUS  
  168.     cout<<"a 的子数组和的最大值是(正确结果应该返回8) "<<MaxSum(a,6)<<endl;  
  169.     cout<<"b 的子数组和的最大值是(正确结果应该返回10) "<<MaxSum(b,6)<<endl;  
  170.     cout<<"c 的子数组和的最大值是(正确结果应该返回10) "<<MaxSum(c,6)<<endl;  
  171.     cout<<"c 的子数组和的最大值是(正确结果应该返回-2) "<<MaxSum(d,5)<<endl;  
  172.     cout<<"e 的子数组和的最大值(正确结果应该返回6) "<<MaxSum(e,3)<<endl;  
  173. #endif  
  174. #ifdef RETURN_ZERO  
  175.     cout<<"a 的子数组和的最大值是(正确结果应该返回8) "<<MaxSum(a,6)<<endl;  
  176.     cout<<"b 的子数组和的最大值是(正确结果应该返回10) "<<MaxSum(b,6)<<endl;  
  177.     cout<<"c 的子数组和的最大值是(正确结果应该返回10) "<<MaxSum(c,6)<<endl;  
  178.     cout<<"c 的子数组和的最大值是(正确结果应该返回0) "<<MaxSum(d,5)<<endl;  
  179.     cout<<"e 的子数组和的最大值(正确结果应该返回6) "<<MaxSum(e,3)<<endl;  
  180. #endif  
  181.     system("PAUSE");  
  182.     return 0;  
  183. }  
  184. #ifdef MAIN  
  185. #endif  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值