编程之美读书笔记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

时间: 2024-11-08 11:31:08

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

编程之美读书笔记2.15 - 子数组之和的最大值(二维)

http://blog.csdn.net/pipisorry/article/details/39083073 问题: 求二维数组(矩阵)的子矩阵之和的最大值. 亦可见:http://poj.org/problem?id=1050 解法1:(解释见注释) 每个子矩阵由列长.行长和左上角的元素位置决定.如果我们指定左上角的元素位置 (i,j) 和列长 c,那么可以求所有这些子矩阵中和最大的.然后,变化列长 c,可以求以 (i,j) 为左上角的最大和子矩阵.最所有左上角位置再求最大和子矩阵,问题就解

【编程之美】求数组的子数组之和的最大值

一个有N个整数元素的一维数组A[0],A[1],......,A[n-1],这个数组当然有很多子数组,那么子数组的最大值是什么呢? 分析与解法 我们先明确题意: 1. 题目说的子数组,是连续的: 2. 题目只需要求和,并不需要返回子数组的具体位置: 3. 数组中的元素是整数,所以数组可能包含有正整数.零.负整数: 4. 子数组不为空. 解法一:枚举 最简单的办法就是枚举所有的i和j,计算sum[i..j] = A[i]+A[i+1]+...+A[j],遍历所有可能的sum[i..j],找到最大值

编程之美之2.14 求数组的子数组之和的最大值

[题目] 一个有N个整数元素的一维数组(A[0],A[1],A[2],...A[n-1]),这个数组中当然有很多子数组,那么子数组之和的最大值是多少? 该子数组是连续的. 我们先来明确一下题意: (1)子数组意味着是连续的. (2)题目只需要求和,并不需要返回子数组的具体位置. (3)数组的元素是整数,所以数组可能包含正整数,负整数或者零. 举几个例子: 数组:[1,-2,3,5,-3,2]返回8 数组:[0,-2,3,5,-1,2]返回9 数组:[-9,-2,-3,-5,-3]返回8 [解法一

编程之美读书笔记1.8 - 小飞的电梯调度算法

http://blog.csdn.net/pipisorry/article/details/36688019 问题: 亚洲微软研究院所在的希格玛大厦一共有6部电梯.在高峰时间,每层都有人上下,电梯每层都停.实习生小飞常常会被每层都停的电梯弄的很不耐烦,于是他提出了这样一个办法: 由于楼层并不算太高,那么在繁忙的上下班时间,每次电梯从一层往上走时,我们只允许电梯停在其中的某一层.所有乘客从一楼上电梯,到达某层后,电梯停下来,所有乘客再从这里爬楼梯到自己的目的层.在一楼的时候,每个乘客选择自己的目

《编程之美-读书笔记》-1 中国象棋将帅问题

时间:2014.05.27 地点:基地 ---------------------------------------------------------------------------------------- 一.指针和引用的区别 1.指针可以为空,引用不可以不空. 引用是一个对象的别用,定义一个引用时必须初始化,而声名指针时可以不指向任何对象,故使用指针时也常要做空的判断,而引用无需,因为引用总是绑定着一个对象. 2.指针可以改变指向,而引用不可以重新绑定新对象.(指针变异思迁,引用从

编程之美读书笔记1.2——中国象棋将帅问题

http://blog.csdn.net/pipisorry/article/details/36380669 问题:下过中国象棋的朋友都知道,双方的"将"和"帅"相隔遥远,并且它们不能照面.在象棋残局中,许多高手能利用这一规则走出精妙的杀招.假设棋盘上只有"将"和"帅"二子(如图1-3所示)(为了下面叙述方便,我们约定用A表示"将",B表示"帅"): A.B二子被限制在己方3×3的格子

[编程之美] 2.14 求数组的子数组之和的最大值

问题描述:给定一个包含N个整数的数组,求数组的子数组之和的最大值. 这是递归和贪心策略的一个经典问题.现在,对这个问题进行一下总结. 1 明确题意 题目中的子数组要求是连续的,也就是数组中的某个连续部分. 如果数组中都是正整数,直接相加就行.因此,主要是要考虑负数的情况. 2 直接求所有的子数组和 最简单且容易理解的解法是求出所有的子数组和,然后保存最大的和. int MaxSum(int *A, int n) { int maximum = -INF; int sum = 0; int i =

编程之美 2.14求数组的子数组之和的最大值

对于一个有N个元素的数组,a[0]~a[n-1],求子数组最大值. 如:数组A[] = [−2, 1, −3, 4, −1, 2, 1, −5, 4],则连续的子序列[4,−1,2,1]有最大的和6. 方法一:暴力 循环遍历,输出所有,判断最大的和 1 #include"iostream" 2 #define MAX 1001 3 using namespace std; 4 5 int main(){ 6 int n, a[MAX], sum , maxsum ; 7 8 cin &

编程之美2.14 求数组的子数组之和的最大值

问题描述: 一个有N个整数元素的一维数组(A[0], A[1], A[2],...,A[n-1]),这个数组当然有很多子数组,那么子数组之和的最大值是什么呢? 解法: 1. 暴力解法-------O(N^3) 2. 改进版暴力解法-------O(N^2) *3. 分治算法-------O(NlogN)(暂时未去实现) 4. 数组间关系法-------O(N) 具体思路和代码: 1.暴力解法 思路:Sum[i,...,j]为数组第i个元素到第j个元素的和,遍历所有可能的Sum[i,...,j].