当前位置:   article > 正文

最大子段和问题算法设计(C语言)_最大子段和c语言代码

最大子段和c语言代码

编译环境:Dev-C++

分别用暴力枚举,优化枚举,递归分治和动态规划的方法解决最大字段和问题。


最大字段和问题描述:

给定n个整数(可能为负整数)组成的序列a1,a2,…,an,求该序列连续的子序列和的最大值。 如果该子段的所有元素和是负整数时定义其最大子段和为0。

最大子段和问题的形式化描述:


算法思想

(1)暴力枚举法思想:

用一个三重循环,i和j从1到n分别表示每一次加法加数和最后一个被加数的下标,k从i到j用来做a[i]到a[j]求和,每次循环加和定义一个当前最大子段和thissum和sum比较替换;


(2)优化枚举法思想:

在暴力枚举下,我们很容易想到可以把k这一重循环省略,避免了重复加和,例如:要求sum[3,8],只需要在sum[3,7]的基础上加a[8],而不需要再从a[0]加到a[8],从而提高效率。


(3)分治法思想:

将原数组a[left,right]分为长度相同的两段子数组a[left,mid]和a[mid+1,right];

然后利用递归分别求解左半段和又半段的最大子段和;

这个时候分成三种情况:

第一种情况是:最大子段和为左半段最大字段和;

第二种情况是:最大子段和为右半段最大字段和;

第三种情况是:跨中间的最大字段和;求解思想见下图: 

最后把三种情况的最大子段和做比较就可以得出最大子段和。


(4)动态规划法:

之所以用到了动态规划的思想,是因为问题具有最小子结构性质,而且子问题之间有所重复,原问题的最优解可以由此问题从底向上推导出来

 

定义一个f数组复制a数组,f[i]表示以a[i]为结束的子数组最大和,f[i]一定是包涵a[i]的,所以如果前面的子问题最优解是小于0,那么包涵以a[i]为结尾的最大字段和,就是它自己,而如果前面的子问题最优解大于或者等于0,则把a[i]加上f[i-1]就是他的最大子段和;  


 算法调试过程及输入输出结果:

测试用例:

Input:

      7

      1 -5 2 4 5 -1 7

Output:

      17

      2 4 5 -1 7 

 

可以看到,通过一步步简化,时间复杂度从O(n^3)到O(n^2)到O(nlogn)再到O(n),时间复杂度不断改进,这就是算法的魅力! 


源代码:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<malloc.h>
  4. /*测试用例:
  5. Input:
  6. 7
  7. 1 -5 2 4 5 -1 7
  8. Output:
  9. 17
  10. 2 4 5 -1 7
  11. */
  12. //暴力枚举法
  13. int MaxSubSum_Violent(int *a,int n,int& besti,int& bestj){
  14. int sum=0;
  15. for(int i=0;i<n;i++){
  16. for(int j=0;j<n;j++){
  17. int thissum=0;
  18. for(int k=i;k<=j;k++){
  19. thissum+=a[k];
  20. }
  21. if(thissum>sum){
  22. sum=thissum;
  23. besti=i;
  24. bestj=j;
  25. }
  26. }
  27. }
  28. return sum;
  29. }
  30. //优化枚举法
  31. int MaxSubSum_Optimized(int *a,int n,int &besti,int &bestj){
  32. int sum=0;
  33. for(int i=0;i<n;i++){
  34. int thissum=0;
  35. for(int j=i;j<n;j++){
  36. thissum+=a[j];
  37. if(thissum>sum){
  38. sum=thissum;
  39. besti=i;
  40. bestj=j;
  41. }
  42. }
  43. }
  44. return sum;
  45. }
  46. //分治法
  47. int CrossSubArray(int *a,int left,int mid,int right){
  48. int leftsumMax=0,rightsumMax=0,temp=0;;
  49. for(int i=mid;i>=left;i--){
  50. temp+=a[i];
  51. if(temp>=leftsumMax){
  52. leftsumMax=temp;
  53. }
  54. }
  55. temp=0;
  56. for(int j=mid+1;j<=right;j++){
  57. temp+=a[j];
  58. if(temp>=rightsumMax){
  59. rightsumMax=temp;
  60. }
  61. }
  62. int crosssum=leftsumMax+rightsumMax;
  63. return crosssum;
  64. }
  65. int MaxSubSum_DivideConquer(int *a,int left,int right){
  66. int sum=0;
  67. if(left==right){
  68. sum=a[left];
  69. }else{
  70. int mid=(left+right)/2;
  71. int leftsum=MaxSubSum_DivideConquer(a,left,mid);
  72. int rightsum=MaxSubSum_DivideConquer(a,mid+1,right);
  73. int crosssum=CrossSubArray(a,left,mid,right);
  74. if(leftsum>=rightsum&&leftsum>=crosssum){
  75. sum=leftsum;
  76. }else if(rightsum>=leftsum&&rightsum>=crosssum){
  77. sum=rightsum;
  78. }else if(crosssum>=rightsum&&crosssum>=leftsum){
  79. sum=crosssum;
  80. }
  81. }
  82. return sum;
  83. }
  84. //动态规划
  85. int MaxSubSum_DynamicProgram(int *a,int n){
  86. int Max_sum=0;
  87. int *f=(int *)malloc(sizeof(int)*(n+1));
  88. for(int i=0;i<n;i++){
  89. f[i]=a[i];
  90. }
  91. for(int i=1;i<=n;i++){
  92. if(f[i-1]+a[i]>=a[i]) f[i]=f[i-1]+a[i];
  93. if(f[i-1]+a[i]<a[i]) f[i]=a[i];
  94. if(f[i]>=Max_sum) Max_sum=f[i];
  95. }
  96. // for(int i=1;i<=n;i++){
  97. // if(f[i-1]>=0) f[i]=f[i-1]+a[i];
  98. // if(f[i-1]<0) f[i]=a[i];
  99. // if(f[i]>=Max_sum) Max_sum=f[i];
  100. // }
  101. return Max_sum;
  102. }
  103. int main(){
  104. int n=0;
  105. printf("请输入原数组的长度:");
  106. scanf("%d",&n);
  107. int *a=(int*)malloc(sizeof(int)*(n+1));
  108. if(!a) exit(-2);
  109. puts("请输入原数组:");
  110. for(int i=0;i<n;i++){
  111. scanf("%d",a+i);
  112. }
  113. int besti,bestj;
  114. puts("使用暴力枚举法的结果是");
  115. printf("最大子段和为:%d\n",MaxSubSum_Violent(a,n,besti,bestj));
  116. puts("对应的子数组为:");
  117. for(int i=besti;i<=bestj;i++){
  118. printf("%d ",a[i]);
  119. }
  120. putchar('\n');
  121. puts("--------------------------------");
  122. puts("使用优化枚举法的结果是");
  123. printf("最大子段和为:%d\n",MaxSubSum_Optimized(a,n,besti,bestj));
  124. puts("对应的子数组为:");
  125. for(int i=besti;i<=bestj;i++){
  126. printf("%d ",a[i]);
  127. }
  128. putchar('\n');
  129. puts("--------------------------------");
  130. puts("使用分治法的结果是");
  131. printf("最大子段和为:%d\n",MaxSubSum_DivideConquer(a,0,n));
  132. puts("--------------------------------");
  133. puts("使用动态规划法的结果是");
  134. printf("最大子段和为:%d\n",MaxSubSum_DynamicProgram(a,n));
  135. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/951835
推荐阅读
相关标签
  

闽ICP备14008679号