您的位置:首页 > 其它

【算法设计与分析】6、最大字段和

2014-12-07 19:02 399 查看
/**
* 书本:《算法分析与设计》
* 功能:若给定n个整数组成的序列a1, a2, a3, ......an, 求该序列形如ai+a(i+1)+......+an的最大值
* 文件:MaxSum.cpp
* 时间:2014年11月30日17:37:26
* 作者:cutter_point
*/

#include <iostream>

using namespace std;

//方法一:
/*
简单的解决方式时间复杂度是O(n^3)
解决方法就是一般算法
*/
//给我一串数字元素,找到最大的字段和的那个字段
//n是这串数字的个数, a是这串数字, besti是最佳的开始加的位置,bestj是最佳的结束位置
int MaxSum(int n, int *a, int& besti, int& bestj)
{
	int sum = 0;	//统计得到的最大值,从中选出最大的
	for (int i = 1; i <= n; ++i)	//从第一个数字开始,到最后一个数
	{
		for (int j = i; j <= n; ++j)	//j是表明要在i开始到j的累加范围
		{
			int thissum = 0;	//一这个i起始的开始向后加到j的当前这个i之后对应的最大字段和
			//开始从i开始往后一个一个的加,并把选到最大的那个字段和
			for (int k = i; k <= j; ++k)	//重这个i加到j(j这个值每个都会试到)
				thissum += a[k];

			if (thissum > sum)
			{
				sum = thissum;	//把新的最大值放给要返回的值
				besti = i;	//最好的起始位置给记住
				bestj = j;	//最好的结束位置给记住
			}

		}
	}

	return sum;	//返回最大值
}

//方法二:
/*
简单的解决方式时间复杂度是O(n^2)
解决方法就是运用一个数学公式i到j的累加 = 第j个数 + 前面的j-1个和
*/
//给我一串数字元素,找到最大的字段和的那个字段
//n是这串数字的个数, a是这串数字, besti是最佳的开始加的位置,bestj是最佳的结束位置
int MaxSum2(int n, int *a, int& besti, int& bestj)
{
	int sum = 0;	//还是这个是得到最终的结果
	//还是从第i个开始向后加j,知道所有的i都得到比较
	for (int i = 1; i <= n; ++i)
	{
		//这个i对应的最大值是
		int thissum = 0;
		for (int j = i; j <= n; ++j)
		{
			thissum += a[j];	//从i开始累加
			if (thissum > sum)	//只要找到比上一个的sum大的就代入
			{
				sum = thissum;
				besti = i;
				bestj = j;
			}
		}

	}

	return sum;
}

//方法三:
/*
简单的解决方式时间复杂度是O(nlogn)
解决方法就是运用分治递归的方式
就是最优子结构是

a[1:n]的最大字段是a[1:n/2]相同的
a[1:n]的最大字段是a[n/2+1:n]相同的
a[1:n]的最大字段和为i到j的累加,且i和j分别在中点的两端

*/
//给我一串数字元素,找到最大的字段和的那个字段
//a里面是这一串数字,left是这串数字的开始位置,right是这串数字的结束位置
int MaxSubSum(int *a, int left, int right)
{
	int sum = 0; //还是用来计数最大值
	//如果只有一个数了,那么就开是比0大还是比0小,大就是这个数了,小那么还是不用加了就等于0把
	if (left == right)	//还有这一步是为了方便递归
		sum = a[left] > 0 ? a[left] : 0;
	else	//如果不止一个数的话
	{
		int center = (left + right) / 2;
		//1、得到左边的最大值
		int leftsum = MaxSubSum(a, left, center);
		//2、得到右边的最大值
		int rightsum = MaxSubSum(a, center + 1, right);

		//3、如果左边和右边夹在一起合成的字段和的话
		//用一个数来表示左边正数的和
		int s1 = 0;	//从右到左得到最大的那个值
		int lefts = 0;	//依次加上左边的下一个值得和
		for (int i = center; i >= left; --i)	//从中间向左边叠加,为何和右边的合并所以必须这样加
		{
			lefts += a[i];	//依次加到lefts里面
			if (lefts > s1)
				s1 = lefts;	//如果这个加起来的和,从右到左得到最大的那个值
		}

		//右边同上
		int s2 = 0; //从左到右得到最大的那个值
		int rights = 0; //依次加上右边的下一个值得和
		for (int i = center + 1; i <= right; ++i) //从中间向右边叠加,为何和左边的合并所以必须这样加
		{
			rights += a[i]; //依次加到lefts里面
			if (rights > s2)
				s2 = rights;
		}

		//第三种方式的和是
		sum = s1 + s2;

		//比较三种方式的大小,选出最大的那个
		if (sum < leftsum) sum = leftsum;
		if (sum < rightsum) sum = rightsum;
	}

	return sum;
}

//迭代结束之后得到最大值的方式
//n是数字的个数,a是这串数字的长度
int MaxSum3(int n, int *a)
{
	return MaxSubSum(a, 1, n);
}

int main()
{
	int a[] = { 0, -1, 7, -3, -3, 5, -2, 7 };
	//得到数组的长度
	int n = sizeof(a) / sizeof(*a)-1;
	int besti = 0, bestj = 0;

	cout << "这串数字是:" << endl;
	for (int i = 1; i <= n; ++i)
		cout << a[i] << " ";

	cout << endl;

	cout << "最长字段和是:" << MaxSum(n, a, besti, bestj);
	cout<< " 从" << besti << "到" << bestj << endl;
	cout << "最长字段和是:" << MaxSum2(n, a, besti, bestj);
	cout << " 从" << besti << "到" << bestj << endl;
	cout << "最长字段和是:" << MaxSum3(n, a)<<endl;

	return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: