编程之美系列之求子数组的最大和(续)

在上一篇博客 编程之美系列之求子数组的最大和中给出了连续子数组最大和的求法。现在把题目扩展一下,有以下两种情况。
PS:转载请注明出处: http://blog.csdn.net/kay_zhyu/article/details/8877826
1、这个数组是循环的,也就是说数组首尾相连,找出连续子数组的最大和,那这个最大和可能就有一部分在最左边,有一部分在最右边。有两种处理方法:
*在数组的后面将所有的元素复制一遍,那就得到一个新的数组,这样就相当于将数组的首尾连接起来了。然后按照上一篇博客的方法,对新数组从头遍历到尾就OK了。这个方法有什么坏处呢?首先我们复制元素到数组尾的时候,内存万一不够怎么办?还有这里我们其实改变的原始数据,一般来说原始数据都是不允许改变的。
*前一种方法相当于遍历了两遍数组。既然要遍历两遍,那就按照上一篇博客中的方法,做两次循环,遍历两次数组就可以了。只在第一次遍历的开始赋初值。只给出这种方法的代码,因为更优。
int GetMaxSubSum(int *arr, int nLen)
{
	if(!arr || nLen < 1)
		return -Inf;
	int sum = arr[0];
	int ans = sum;
	int i;
	//第一次遍历
	for(i = 1; i < nLen; ++i)
	{
		sum = max(arr[i], sum + arr[i]);
		ans = max(ans, sum);
	}
	//第二次遍历,相当于由尾到头
	for(i = 0; i < nLen; ++i)
	{
		sum = max(arr[i], sum + arr[i]);
		ans = max(ans, sum);
	}
	return ans;
}
2、如果数组是二维的,现在需要寻找这个二维数组的一个连续的子数组的最大和,怎么搞?受前面思想的启发,可以把问题抽象成一维的情况。怎么抽象呢?如果我们知道了要取第Begin行到End行的数据,那把第Line列的Begin到End的和最为一维数组的元素,这样是不是就是一个一维的求最大和的问题啦!然后用两层循环来遍历Begin和End的所有情况就可以了。当然这里需要反复的求和,为了节省时间,预先算好二维数组的部分和PartSum,其中PartSum[i][j]表示从(0,0)到(i,j)子矩阵的和。则第Line列的第Begin到End行的和为PartSum[End][Line] - PartSum[Begin-1][Line] - PartSum[End][Line - 1] + PartSum[Begin-1][Line - 1];
//得到数组的部分和
void GetPartSum(int* arr, int* PartSum)
{
	if(!arr || !PartSum)
		return;
	int i,j;
	for(i = 0; i < m; ++i)
	{
		for(j = 0; j < n; ++j)
		{
			PartSum[i * n + j] = arr[i * n + j];
			if(i > 0 && j > 0)
				PartSum[i * n + j] += PartSum[(i - 1) * n +j] + PartSum[i * n + j - 1] 
									- PartSum[(i - 1) * n + j - 1];
			else
			{
				if(i > 0)
					PartSum[i * n + j] += PartSum[(i - 1) * n +j];
				if(j > 0)
					PartSum[i * n + j] += PartSum[i * n + j - 1];
			}				 
		}//end for j
	}//end for i
}
//得到第Line列,第Begin行到End行的和
int GetPartSum(int* PartSum, int Begin, int End, int Line)
{
	if(Begin > 0 && Line > 0)
		return PartSum[End * n + Line] - PartSum[(Begin - 1) * n + Line]
		- PartSum[End * n + Line - 1] + PartSum[(Begin - 1) * n + Line - 1];
	else if(Begin > 0)
		return PartSum[End * n + Line] - PartSum[(Begin - 1) * n + Line];
	else if(Line > 0)
		return PartSum[End * n + Line] - PartSum[End * n + Line - 1];
	else
		return PartSum[End * n + Line];
}
//得到子数组的最大和
int GetMaxSubSumForTwoDim(int* arr, int* PartSum)
{
	if(!arr || !PartSum || n < 1 || m < 1)
		return -Inf;
	int Begin,End,i;
	int sum,ans,Max;
	Max = -Inf;
	for(Begin = 0; Begin < m; ++Begin)//遍历起始行
	{
		for(End = Begin; End < m; ++End)//遍历终止行
		{
			sum = GetPartSum(PartSum, Begin, End, 0);
			ans = sum;
			//确定好起始行和终止行之后,抽象成一维的情况
			for(i = 1; i < n; ++i)
			{
				if(sum < 0)
					sum = 0;
				sum += GetPartSum(PartSum, Begin, End, i);
				ans = max(ans, sum);
			}//end for i
		}//end for End
		Max = max(ans, Max);
	}//end for Begin
	return Max;
}
题目扩展:如果题目变成了求给定维数,比如说2*3,和最大的子矩阵,直接套上面的思路就可以了,这里不废话了。最后给出一些辅助函数及变量的定义,以及main函数的调用,不需要的可以pass:
#include<stdio.h>
const int Inf = 1e5;
int m,n;
inline int max(const int a, const int b)
{
	return a > b ? a : b;
}
int main()
{
	const int N = 20;
	const int M = 20;
	int arr[M * N];
	int PartSum[M * N];
	int i,j;
	while(scanf("%d %d", &m, &n) != EOF)
	{
		for(i = 0; i < m; ++i)
			for(j = 0; j < n; ++j)
				scanf("%d", &arr[i * n + j]);
		GetPartSum(arr, PartSum);
		printf("%d\n", GetMaxSubSumForTwoDim(arr, PartSum));
	//	printf("%d\n", GetMaxSubSum(arr, n));
	}
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值