[算法]最大子段和问题

给定长度为n的整数序列,a[1…n], 求[1,n]某个子区间[i , j]使得a[i]+…+a[j]和最大.或者求出最大的这个和.例如(-2,11,-4,13,-5,2)的最大子段和为20,所求子区间为[2,4].


解题思路

  • 穷举
    通过三重循环遍历所有的子段可能,取得最大和时的子段下标,简单但低效,并且当序列范围太大时,可能会超时。
int begin = 0;
int end = 0;
int max_sum = 0;
for (int i = 0; i < n; i++) {
    for (int j = i; j < n; j++) {
        int sum = 0;
        for (int k = i; k <= j; k++) {
            sum += array[k];
        }
        if (sum > max_sum) {
            max_sum = sum;
            begin = i;
            end = j;
        }
    }
}

但是我们可以通过优化O(n^3)的穷举代码,消除第三个循环,使它变成O(n^2)复杂度。

int begin = 0;
int end = 0;
int max_sum = 0;
for (int i = 0; i < n; i++) {
    int sum = 0;        
    for (int j = i; j < n; j++) {
        sum += array[j];
        if (sum > max_sum) {
            max_sum = sum;
            begin = i;
            end = j;
        }
    }
}
  • 分治
    考虑到子段区间[begin,end]可能存在的情况有三种:
    • 位于[0,n/2]区域内
    • 位于[n/2+1,n-1]区域内
    • 起点位于[0,n/2]区域内,终点位于[n/2+1,n-1]区域内
      那么最后的结果只能是上述三种结果中的最大值,所以可以利用分治的思想,前两种情况使用递归方法,第三种情况使用穷举法求出。
int maxSub(int* n, int left, int right) {
    if (left == right)
        return n[left]>0?n[left]:0;
    int mid = (left+right)/2;
    int leftSub = maxSub(n,left,mid);
    int rightSub = maxSub(n,mid+1,right);

    int sum = 0;
    int leftMax = 0;
    //向左边扩散
    for (int i = mid; i >= left; i--) {
        sum  += n[i];
        if (sum > leftMax) {
            leftMax = sum;
        }
    }
    sum = 0;
    int rightMax = 0;
    //向右边扩散
    for (int i = mid+1; i <= right; i++) {
        sum += n[i];
        if (sum > rightMax) {
            rightMax = sum;
        }
    }
    int ans = leftMax + rightMax;
    ans = max(max(ans,leftSub),max(ans,rightSub));
    return ans;
}

这种分治的方法复杂度是O(nlog n),可以满足大多数样例。
- 动态规划
这里引用一段网上对该问题的动态规划的分析:
这里写图片描述

int* b = new int[n+1];
memset(b,0,n+1);
for (int i = 1; i < n+1; i++) {
    if (b[i-1] > 0) {
        b[i] = b[i-1]+a[i];
    } else {
        b[i] = a[i];
    }
}
return max(b[n],0);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值