快要毕业了,逐步把之前做过的一些算法和数据结构,项目相关资料总结整理一下,一来温习一下之前的知识,二来记录下学习点滴,方便之后的学习。
- 题目描述:给定一个整数数组,数组中有正数也有负数,数组中连续的一个或者多个整数组成一个子数组,每个子数组都有一个和,求给定数组的最大子数组和。也成为“最大子段和”问题。要求时间复杂度为O(n).
该题目是一个简单的DP问题。算法的思路很多,但是如果要满足时间复杂度为O(n)。一般的算法是达不到的。
DP解决该问题的思路是:扫描数组,记录当前数组的和 与 已经记录的最大的子数组的和。如果当前记录的和已经是负数,那么舍弃它,并重新设置当前数组和为当前元素的值。否则,比较当前数组的和与已经记录的最大的子数组的和大小,如果比最大的和大,就更新最大和。扫描一遍完成统计,满足题目的要求。
算法实现如下:
- #include <stdio.h>
- #include <assert.h>
- #include <stdlib.h>
- int maxSubSum(int *a,int n){
- assert(n > 0);
- int max = 0,curSum = 0;
- for(int i = 0;i < n;i++){
- if(curSum > 0){
- curSum += a[i];
- }
- else{
- curSum = a[i];
- }
- if(curSum > max){
- max = curSum;
- }
- }
- return max;
- }
- int main(){
- int a[] = {1,-2,3,10,-4,7,2,-5};
- printf("%d \n",maxSubSum(a,8));
- system("pause");
- return 0;
- }
进一步考虑,如果不仅仅是求最大和,而且要求最大字段的开始和结束索引。那么应该怎么做呢。
算法的思路还是一样的,唯一不同的是,我们需要增加两个变量start和end用来记录开始和结束的索引。并且在发生变化的时候更新相应的值,那么扫描一遍一样可以求出最大子段的开始结束索引。
算法如下
- #include <stdio.h>
- #include <assert.h>
- #include <stdlib.h>
- int maxSubWithIndex(int *a,int n,int &start,int &end){
- assert(n > 0);
- start = end = 0;
- int max = 0,curSum = 0;
- for(int i = 0;i < n;i++){
- if(curSum > 0){
- curSum += a[i];
- }
- else{
- start = i;
- curSum = a[i];
- }
- if(curSum > max){
- end = i;
- max = curSum;
- }
- }
- return max;
- }
- int main(){
- int a[] = {1,-2,3,10,-4,7,2,-5};
- int start = 0,end = 0;
- printf("ok:%d \n",maxSubWithIndex(a,8,start,end));
- printf("starts:%d ends:%d \n",start,end);
- system("pause");
- return 0;
- }
进一步拓展,对于一个二维矩阵,一样存在一个最大的子矩阵,使得该子矩阵的和为最大。思考一下:这个问题跟之前的最大字段和的问题有什么相似之处吗?
编码之前,我们不妨先画个图:

如果把每一列的和都当做一个元素的话,那么问题是不是跟一维的求最大字段和的问题类似呢?我们只需要顺序扫描一次矩阵,并求出每个子矩阵的和,然后与最大的和比较,最后可以求出最大子矩阵的和。
算法对应的代码如下:
- #include <stdio.h>
- #include <assert.h>
- #define N 3
- int maxSum(int *a,int n){
- assert(n > 0);
- int i,sum = 0,max = 0;
- for(i = 0;i<n;i++){
- if(sum > 0){
- sum +=a[i];
- }
- else{
- sum = a[i];
- }
- if(sum >max){
- max = sum;
- }
- }
- return max;
- }
- int maxSubMatrix(int ** a,int m,int n){
- int i,j,k,max = 0,sum = 0;
- int *b = new int[n];
- for(i = 0;i < m;i++){
- for(k = 0;k < n;k++){
- b[k] = 0;
- }
- for(j = i;j < m;j++){
- for(k = 0;k < n;k++){
- b[k] += a[j][k];
- }
- sum = maxSum(b,n);
- if(sum > max){
- max = sum;
- }
- }
- }
- return max;
- }
- main(){
- int m,n;
- while(scanf("%d%d",&m,&n) == 2){
- int **a = new int *[m] ;
- for( int i = 0;i < m;i++){
- a[i] = new int[n];
- for(int j = 0;j < n;j++){
- scanf("%d",&a[i][j]);
- }
- }
- printf("%d\n",maxSubMatrix(a,m,n));
- for(int i = 0;i<m;i++) delete a[i];
- delete a;
- }
- }
本文总结了即将毕业的学生在算法和数据结构方面的工作,包括最大子数组和问题的动态规划解决方案,最大字段的开始和结束索引,二维矩阵最大子矩阵的查找,以及其与一维最大子数组问题的关系。通过实例代码展示了算法实现过程。
1450

被折叠的 条评论
为什么被折叠?



