最大字段和求解方法

问题描述:给定n个整数(可能有负数)组成的序列a1,a2,...,an,求该序列的最大子段和。如果所有整数都是负数,那么定义其最大子段和为0

方法一:暴力双重循环破解法

 

方法二:递归分治

在数组的 center = (right-left)/2+left 位置处分开。形成两个子数组。

那么,最大子段和 可能出现在三个位置:

          a,可能出现在【左子数组】

          b,可能出现在【右子数组】 

          c,可能出现在【过center的 中间某部分元素组成的子数组】。

下面考虑 三种情况的计算方法:

第一种情况: 计算 left 到 center 的最大和,记作 leftSum

第二种情况: 计算从 center+1 到 right的最大和,记作 rightSum

第三种情况: 跨边界的和。 ;以center为中心分别向两边计算和。

      a.从 center出发,每次向左边扩张一步,并且记录当前的值S1,如果当前的和比上次的和大,就更新S1,一直向左扩张到 位置  Left。 

      b.从 center+1出发,每次扩张一步,计算当前的和 为S2,如果当前的值比上次的和 大,那么,就更新S2的值,一直向右扩张到位置Right。

      c.计算过center的连续值的和,S1+S2的值 Sum。 这个就是跨边界的和。

 上面三种情况考虑计算完成后,最后一步就是,比较三个值中的最大值,取最大值就可以了。

最大字段和求解方法_第1张图片

代码部分:

int MaxSubSum(int a[], int left, int right) {
	if (right == left)                //递归结束的标志
		return a[left]>0?a[left]:0;
	int center = (left + right) / 2;
	int leftsum = MaxSubSum(a, left, center);    //左子段的最大值
	int rightsum = MaxSubSum(a, center+1, right);    //右子段的最大值
	int s1 = 0, s2 = 0, cur_t = 0;
	for (int i = center; i >= 0; i--) {            //center到left的连续的最大值
		cur_t += a[i];
		if (cur_t > s1)
			s1 = cur_t;
	}
	cur_t = 0;
	for (int i = center+1; i <= right; i++) {        //center到right的连续的最大值
		cur_t += a[i];
		if (cur_t > s2)
			s2 = cur_t;
	}
	int sum = s1 + s2;                                //sum是过center的连续的最大值
	return leftsum > rightsum ? (leftsum > sum ? leftsum : sum) : (rightsum > sum ? rightsum : sum);                //比较三者的大小,返回最大的那一个
}

 

 

方法三:动态规划

最大字段和求解方法_第2张图片

代码部分:

int MaxSubArray(int a[], int n) {
	int i, b = 0, sum = 0;
	for (i = 0; i < n; i++) {
		if (b > 0)				//b看作b[i-1],如果b是负数那肯定没有a[i]大 b=max(b[i-1]+a[i], a[i])
			b += a[i];
		else
			b = a[i];

		if (b > sum)			//取得b的极值
			sum = b;
	}
	return sum;
}

 

参考文献:

https://www.cnblogs.com/TIMHY/p/8284989.html

https://www.cnblogs.com/lixing-nlp/p/7629403.html

你可能感兴趣的:(算法)