【算法总结-DP】求子数组的最大和

本文总结了即将毕业的学生在算法和数据结构方面的工作,包括最大子数组和问题的动态规划解决方案,最大字段的开始和结束索引,二维矩阵最大子矩阵的查找,以及其与一维最大子数组问题的关系。通过实例代码展示了算法实现过程。

    快要毕业了,逐步把之前做过的一些算法和数据结构,项目相关资料总结整理一下,一来温习一下之前的知识,二来记录下学习点滴,方便之后的学习。

[plain]  view plain copy print ?
  1. 题目描述:给定一个整数数组,数组中有正数也有负数,数组中连续的一个或者多个整数组成一个子数组,每个子数组都有一个和,求给定数组的最大子数组和。也成为“最大子段和”问题。要求时间复杂度为O(n).  

    该题目是一个简单的DP问题。算法的思路很多,但是如果要满足时间复杂度为O(n)。一般的算法是达不到的。

DP解决该问题的思路是:扫描数组,记录当前数组的和 与 已经记录的最大的子数组的和。如果当前记录的和已经是负数,那么舍弃它,并重新设置当前数组和为当前元素的值。否则,比较当前数组的和与已经记录的最大的子数组的和大小,如果比最大的和大,就更新最大和。扫描一遍完成统计,满足题目的要求。

算法实现如下:

  1. #include <stdio.h>  
  2. #include <assert.h>  
  3. #include <stdlib.h>  
  4.   
  5. int maxSubSum(int *a,int n){  
  6.     assert(n > 0);  
  7.     int max = 0,curSum = 0;  
  8.     for(int i = 0;i < n;i++){  
  9.         if(curSum > 0){  
  10.             curSum += a[i];  
  11.         }  
  12.         else{  
  13.             curSum = a[i];  
  14.         }  
  15.         if(curSum > max){  
  16.             max = curSum;  
  17.         }  
  18.     }  
  19.     return max;  
  20. }  
  21.   
  22. int main(){  
  23.     int a[] = {1,-2,3,10,-4,7,2,-5};  
  24.     printf("%d \n",maxSubSum(a,8));  
  25.     system("pause");  
  26.     return 0;  
  27.   
  28. }  

进一步考虑,如果不仅仅是求最大和,而且要求最大字段的开始和结束索引。那么应该怎么做呢。

算法的思路还是一样的,唯一不同的是,我们需要增加两个变量start和end用来记录开始和结束的索引。并且在发生变化的时候更新相应的值,那么扫描一遍一样可以求出最大子段的开始结束索引。

算法如下

  1. #include <stdio.h>  
  2. #include <assert.h>  
  3. #include <stdlib.h>  
  4.   
  5. int maxSubWithIndex(int *a,int n,int &start,int &end){  
  6.     assert(n > 0);  
  7.     start = end = 0;  
  8.     int max = 0,curSum = 0;  
  9.     for(int i = 0;i < n;i++){  
  10.         if(curSum > 0){  
  11.             curSum += a[i];  
  12.         }  
  13.         else{  
  14.             start = i;  
  15.             curSum = a[i];  
  16.         }  
  17.         if(curSum > max){  
  18.             end = i;  
  19.             max = curSum;  
  20.         }  
  21.     }  
  22.     return max;  
  23. }  
  24.   
  25. int main(){  
  26.     int a[] = {1,-2,3,10,-4,7,2,-5};  
  27.     int start = 0,end = 0;  
  28.     printf("ok:%d \n",maxSubWithIndex(a,8,start,end));  
  29.     printf("starts:%d ends:%d \n",start,end);  
  30.     system("pause");  
  31.     return 0;  
  32.   
  33. }  

进一步拓展,对于一个二维矩阵,一样存在一个最大的子矩阵,使得该子矩阵的和为最大。思考一下:这个问题跟之前的最大字段和的问题有什么相似之处吗?

编码之前,我们不妨先画个图:

如果把每一列的和都当做一个元素的话,那么问题是不是跟一维的求最大字段和的问题类似呢?我们只需要顺序扫描一次矩阵,并求出每个子矩阵的和,然后与最大的和比较,最后可以求出最大子矩阵的和。

算法对应的代码如下:

  1. #include <stdio.h>  
  2. #include <assert.h>  
  3. #define N 3  
  4. int maxSum(int *a,int n){  
  5.         assert(n > 0);  
  6.         int i,sum = 0,max = 0;  
  7.     for(i = 0;i<n;i++){  
  8.         if(sum > 0){  
  9.             sum +=a[i];  
  10.         }  
  11.         else{  
  12.             sum = a[i];  
  13.         }  
  14.         if(sum >max){  
  15.             max = sum;  
  16.         }  
  17.     }  
  18.     return max;  
  19. }  
  20.   
  21. int maxSubMatrix(int ** a,int m,int n){  
  22.     int i,j,k,max = 0,sum = 0;  
  23.     int *b = new int[n];  
  24.   
  25.     for(i = 0;i < m;i++){  
  26.         for(k = 0;k < n;k++){  
  27.             b[k] = 0;  
  28.         }  
  29.         for(j = i;j < m;j++){  
  30.             for(k = 0;k < n;k++){  
  31.                 b[k] += a[j][k];  
  32.             }  
  33.             sum = maxSum(b,n);  
  34.             if(sum > max){  
  35.                 max = sum;  
  36.             }  
  37.         }  
  38.     }  
  39.     return max;  
  40. }  
  41.   
  42. main(){  
  43.     int m,n;  
  44.     while(scanf("%d%d",&m,&n) == 2){  
  45.         int **a = new int *[m] ;  
  46.         forint i = 0;i < m;i++){  
  47.             a[i] = new int[n];  
  48.             for(int j = 0;j < n;j++){  
  49.                 scanf("%d",&a[i][j]);  
  50.             }  
  51.         }  
  52.         printf("%d\n",maxSubMatrix(a,m,n));  
  53.         for(int i = 0;i<m;i++) delete a[i];  
  54.         delete a;  
  55.     }  
  56.   
  57. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值