最大子数组问题

问题基于《算法导论》第四章 分治策略中最大收益问题


问题原型:

假如你能获取股票未来的行情,怎么计算出什么时候买入,什么时候卖出才能获得最大收益。


首先分析数据后认为在最低处购买向后找到最高点,或者在最高处卖出向前找到价格最低的点。对比这两个点大小。

书中给出了反例,证明该方法并不能准确找出最大收益方案,反例如下:

天数01234
价格10117106
变化 1-43-4
该例子中并不能以之前两种猜想获取最大收益


暴力求解方案

遍历所有买入,卖出可能性进行比较。这种方案当然可行但是其运行时间为Ω(n^2),C++代码如下:

int x[10] = { 0, 2, -4, -1, 3, 2, -5, 8, -1, 3 };

void normal()
{
	int start = 0;
	int end = 0;
	float shouyi = 0.0f;
	float temp = 0.0f;
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			if (i < j)
			{
				for (int m = 0; m < j - i+1; m++)temp += x[m + i];
				if (temp > shouyi)
				{
					shouyi = temp;
					start = i;
					end = j;
				}
				temp = 0;
			}
		}
	}
	cout << "start:" << start << endl << "end:" << end << endl << "shouyi:" << shouyi << endl;
}


可以输出结果

start=7

end=9

shouyi=10

这种方案当然不是最优解。


最大子数组方案

不再考虑输入数组,而是关心价格变化,此时,只需要找到一个起始为i终止为j的子数组,其和最大即可。
但如果任然用简单的循环方式也并不能简化运算,此时可以将数组拆分为两段。
原数组x
02-4-132-58-13

子数组1
02-4-13
start---mid
子数组2
2-58-13
mid--end

此时有三种可能:
1.最大子数组在子数组1范围内:start<=i<j<=mid
2.最大子数组在子数组2范围内:mid<=i<j<=end
3.最大子数组需要穿过子数组1,2:start<=i<=mid<=j<=end

在例子中 第一种情况需要循环25次
void findleft(int x[],int mid,int length)//在左边子数组
{
	int start = 0;
	int end = 0;
	int temp = 0;
	int shouyi = 0;

	for (int i = 0; i < mid; i++)
	{
		for (int j = 0; j < mid; j++)
		{
			if (i < j)
			{ 
				for (int m = 0; m < j - i+1; m++)temp += x[i + m];
				if (temp > shouyi)
				{
					shouyi = temp;
					start = i;
					end = j;
				}
				temp = 0;
			}
		}
	}
	cout << "left" << endl;
	cout << "start:" << start << endl << "end:" << end << endl << "shouyi:" << shouyi << endl;
}
寻找右半段子数组也需要25次:
void findright(int x[],int mid,int length)//在右边子数组
{
	int start = 0;//起始数组下标
	int end = 0;//终止数组下标
	int temp = 0;//临时存储收益值
	int shouyi = 0;//最大收益值

	for (int i = mid; i < length; i++)
	{
		for (int j = mid; j < length; j++)
		{
			if (i < j)
			{
				for (int m = 0; m < j - i+1; m++)temp += x[i + m];
				if (temp > shouyi)
				{
					shouyi = temp;
					start = i;
					end = j;
				}
				temp = 0;
			}
		}
	}
	cout << "right" << endl;
	cout << "start:" << start << endl << "end:" << end << endl << "shouyi:" << shouyi << endl;
}

对于第三种情况因为多了必须穿过中间点的限制,所以也需要25次循环
void findcross(int x[],int mid,int length)//越过中心点
{
	int start = 0;//起始数组下标
	int end = 0;//终止数组下标
	int temp=0;//临时存储收益值
	int shouyi = 0;//最大收益值

	for (int i = 0; i < mid; i++)
	{
		for (int j = mid; j < length; j++)
		{
			for (int m = 0; m < j - i+1; m++)
				temp += x[i + m];
			if (temp > shouyi)
			{
				shouyi = temp;
				start = i;
				end = j;
			}
			temp = 0;
		}
	}
	cout << "cross" << endl;
	cout << "start:" << start << endl << "end:" << end << endl << "shouyi:" << shouyi << endl;
}



总循环数为75(3/4*n^2),而暴力破解需要100(n^2)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值