编程之美---求首尾相连数组子数组之和的最大值

问题描述:
如果数组首尾相连,即允许找到一组数字(A[i],···,A[n-1], A[0],···, A[j]),请使其和最大,怎么办?
问题求解:
问题的解可以分为两种情况:
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,lpos对应于整体思路中的i,rpos对应于整体思路中的j

#include <iostream>
using namespace std;

//*********************************动态规划*******************************:
//假设A[0],A[1],...A(n-1)的最大子段为A[i],...,A[j],则有以下3种情况,
//1)当0=i=j的时候,元素A[0]本身构成和最大的一段
//2)当0=i<j的时候,和最大的一段以A[0]开头
//3)当0<i时候,元素A[0]跟和最大的一段没有关系
//则原始问题A[0],A[1],...A(n-1)的解All[0]=max{A[0],A[0]+Start[1],ALL[1]}
//求得A[0],A[1],...A[n-1](首尾不连接)的情况后再考虑整体思路中的第二种情况
//************************************************************************/
int MaxSum(int *A,int length){
    //情况一:先求出A[0],A[1],...A[n-1](首尾不连接)的情况下子数组和最大值nAll
    int nStart=0;
    int nAll=0;
    for(int i=length-1;i>=0;i--){
        nStart=max(0,A[i]+nStart);
        nAll=max(nStart,nAll);
    }

    //情况二:即跨过A[n-1],A[0]
    //2.1:先求A[n-1]结尾的和最大的一段(A[i],...,A[n-1])(0<=i<n)
    int sum=0;
    int ltempmax=-10000000;
    int lpos=length;
    for(int i=length-1;i>=0;i--){
        sum+=A[i];
        if(sum>ltempmax){
            ltempmax=sum;
            lpos=i;
        }
    }
    //2.2:求A[0]开始和最大的一段(A[0],...,A[j])(0<=j<n)
    sum=0;
    int rtempmax=-10000000;
    int rpos=-1;
    for(int i=0;i<length;i++){
        sum+=A[i];
        if(sum>rtempmax){
            rtempmax=sum;
            rpos=i;
        }
    }
    //如果lpos<=rpos,则循环数组中可能出现的子数组最大值要么是A[0]...A[n-1]子
    //数组和的最大值nAll要么是整个数组A[0]...A[n-1]的和再减去A[0]...A[n-1]
    //中子数组和为负数的最小值
    if(lpos<=rpos){
        //求数组中和为负数且的最小值
        int minStart=0;
        int minAll=0;
        for(int i=0;i<length;i++){
            minStart=min(0,A[i]+minStart);
            minAll=min(minStart,minAll);
        }
        int tempmax=ltempmax+rtempmax;
        for(int i=lpos;i<=rpos;i++){
            tempmax-=A[i];
        }
        //比较A[0]...A[n-1]子数组和的最大值nAll跟A[0]...A[n-1]的和再减去
        //A[0]...A[n-1]中子数组和为负数的最小值
        return max(nAll,tempmax-minAll);
    }else{
        //比较A[0]+...+A[j]+A[i]+...+A[n-1]即ltempmax+rtempmax的值跟
        //A[0]...A[n-1]子数组和的最大值nAll
        return max(nAll,ltempmax+rtempmax);
    }
}

int main()
{
    int a[6]={8,-10,60,3,-1,-6};//跨界子数组[8 | 60,3,-1,-6]=64
    int b[6]={1,-2,3,5,-1,2};
    cout<<"a 的子数组和的最大值是(正确结果应该返回64) "<<MaxSum(a,6)<<endl;
    cout<<"b 的子数组和的最大值是(正确结果应该返回10) "<<MaxSum(b,6)<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值