首页 > 代码库 > 编程之美读书笔记2.14 - 子数组之和的最大值

编程之美读书笔记2.14 - 子数组之和的最大值

http://blog.csdn.net/pipisorry/article/details/39083281

问题:

1. 一个由N个整数元素的一维数组,求其所有子数组中元素和的最大值。

2. 如果数组首尾相邻,也就是允许子数组A[i],...,A[n-1],A[0],...,A[j]存在,求其所有子数组总元素和的最大值。



解法1:

/*	O(n^2) 遍历算法	*/
static int maxSubarraySum1(int *a,int a_len){
	int max_sum = INT_MIN, sum;
	for(int i = 0; i < a_len; i++){				//从i开始的子数组的最大和,遍历所有情况
		sum = 0;
		for(int j = i; j < a_len; j++){
			sum += a[j];
			if(sum > max_sum)
				max_sum = sum;
		}
	}
	return max_sum;
}

解法2:

/*	O(nlogn) 分治算法	*/
static int maxSubarraySum2(int *a, int low, int high){			//a[low] a[high]之间最大子段和
	if(low >= high)							//只剩一个元素的时候返回本身为最大值
		return a[low];

	int mid = (high + low) / 2;					//以a[mid]结尾的最大一段数组之和
	int sum = 0, max_sum_left = INT_MIN;
	for(int i = mid; i >= low; i--){
		sum += a[i];
		if(sum > max_sum_left)
			max_sum_left = sum;
	}

	int max_sum_right = INT_MIN;					//以a[mid + 1]开始的最大一段数组之和
	sum = 0;
	for(int i = mid + 1; i <= high; i++){
		sum += a[i];
		if(sum > max_sum_right)
			max_sum_right = sum;
	}
	int max_sum = max_sum_left + max_sum_right;

	int max_sub_left = maxSubarraySum2(a, low, mid);		//数组左段最大子段和
	int max_sub_right = maxSubarraySum2(a, mid + 1, high); 		//数组左段最大子段和
	int max_sub = max_sub_left > max_sub_right ? max_sub_left:max_sub_right;
	max_sum = max_sum > max_sub? max_sum : max_sub;			//数组总的最大子段和
	return max_sum;
}

解法3:

/*	O(n) DP算法	*/
static int maxSubarraySum3(int *a, int n){
	int *start = (int *)malloc(sizeof(a[0]) * n);					//start[i]为从i开始的包含a[i]最大子数组和
	int *all = (int *)malloc(sizeof(a[0]) * n);					//all[i]为从i开始的最大一段数组和
	start[n - 1] = a[n - 1];
	all[n - 1] = a[n - 1];

	for(int i = n - 2; i >= 0; i--){
		start[i] = start[i + 1] + a[i] > a[i]? start[i + 1] + a[i] : a[i];//start[i] = max{a[i], start[i+1]+a[i]}
		all[i] = start[i] > all[i + 1] ? start[i] : all[i + 1];		//all[i] = max{start[i], all[i+1]}
	}
	return all[0];
}
/*	O(n) DP算法(O(1)空间)	*/
static int maxSubarraySum4(int *a, int n){
	int start = a[n - 1];
	int all = a[n - 1];

	for(int i = n - 2; i >= 0; i--){
		start = start + a[i] > a[i]? start + a[i] : a[i];
		all = start > all ? start : all;
	}
	return all;
}

解法4:
/* O(n) 最优算法	*/
/* 
最优的解是只扫描数组一遍,因此时间为 O(n)。假设 x1, x2, ..., xt 是最优解。
那么,显然, 对任何 i <= t,x1, x2,..., xi 之和不可能为负。
否则,砍去这一段,我们可以得到更大的值,这些该段的最优性矛盾。
这就是说,最优解的段前缀不可能为负。而换句话说,如果一个段的和为负,则不可能是最优解的一部分。
一开始,令当前段为从 x1 开始的段,置为空。我们从数组开始向前搜索,并把遇到的数加入当前段 s,同时记录目前遇到的最大和。
这个过程一直持续到加入某个数 xi,使得 s 之和为负,则清空 s,然后以 xi 的下一个元素为当前段的开始,继续向前搜索。
重复这个过程直到数组结束。在实现时,并不需要维护集合 s 并每次都对其对和,而只需要维护一个当前段的和,
当有新元素加入当前段时,更新段的和;当重新开始一个段时,清 0 该段之和。
*/
static int maxSubarraySum(int *a, int n){
	int sum = 0, max_sum = INT_MIN;
	for(int i = 0; i < n; i++){
		sum += a[i];
		if(sum > max_sum)
			max_sum = sum;
		if(sum < 0)							//前缀<0时可以去掉sum的累积和
			sum = 0;
	}
	return max_sum;
}
/* O(n) 最优算法(记录左右边界)	*/
static int maxSubarraySum5(int *a, int n){
	int sum = 0, max_sum = INT_MIN;
	int max_low = 0, max_high = 0;						//最优子数组左右边界
	int low = 0;								//当前非<0前缀的子数组首下标
	for(int i = 0; i < n; i++){
		sum += a[i];
		if(sum > max_sum){
			max_sum = sum;
			max_high = i;
			max_low = low;
		}
		if(sum < 0){							//前缀<0时可以去掉sum的累积和
			sum = 0;
			low = i + 1;
		}
	}
	printf("max_low = %d, max_high = %d\n", max_low, max_high);
	return max_sum;
}

测试:

//***************************************************************************************/
//*	编程之美2.14 —— 求数组的子数组之和的最大值(微软亚研2006)	皮皮 2014-9-4	*/
//***************************************************************************************/
#include <stdio.h>
#include <assert.h>
#include <malloc.h>
#include <limits.h>

int main(){
	assert( freopen("BOP\\maxSubarraySum.in", "r", stdin) );
	int cases;										//测试案例数目
	scanf("%d", &cases);
	while(cases--){
		int n;										//每个案例中数组元素个数
		scanf("%d", &n);
		int *a = (int *)malloc(sizeof(int) * n);
		for(int i = 0; i < n; i++)
			scanf("%d", &a[i]);

		printf("%d\n", maxSubarraySum1(a, n));
		printf("%d\n", maxSubarraySum2(a, 0, n - 1));
		printf("%d\n", maxSubarraySum3(a, n));
		printf("%d\n", maxSubarraySum4(a, n));
		printf("%d\n", maxSubarraySum(a, n));
		printf("%d\n\n", maxSubarraySum5(a, n));
	}
	fclose(stdin);
	return 0;
}


测试案例:

4
7 -2 5 3 -6 4 -8 6
6 1 -2 3 5 -3 2
6 0 -2 3 5 -1 2
5 -9 -2 -3 -5 -3


answer:
8 [1 - 2]
8 [2 - 3]
9 [2 - 5]
-2 [1 - 1]



from:

http://blog.csdn.net/pipisorry/article/details/39083281

ref:

http://blog.csdn.net/pipisorry/article/details/39048485


编程之美读书笔记2.14 - 子数组之和的最大值