MIT:算法导论——2.渐近符号、递归及解法_和连续子数组的最大和、主方法/主定理求时间复杂度

#if 0
三种求解递归式的方法,即算法的O渐近界的方法:
(1)代入法:猜测一个界,然后用 数学归纳法证明 这个界正确。
(2)递归树法:将递归式转换为一棵递归树。其结点表示不同层次的递归调用产生的代价。
然后用边界和技术 求解递归式。
(3)主方法:可求解 形如下面公式的递归式的界——T(n) = aT(n/b) + f(n)。
其中a>=1, b>1,f(n)是一个给定的函数。f(n) = O(n^k)。




分治策略中,我们递归地求解每个问题,每层递归可以分为如下三个步骤:
(1)分解(Divide):将问题划分为一些子问题。子问题的形式与原问题一样,只是规模更小——即递归情况。
(2)解决(Conquer):递归地求解出子问题。如果子问题足够小,则停止递归,直接求解——即基本情况。
(3)合并(Combine):将子问题的解组合合成原问题的解。
如该例子中跨越中间位置的子数组,并不与原问题完全一样,将该子问题看做合并步骤的一部分。


INPUT:int a[] = { 13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7 };
OUTPUT:7 10 43
其中,7为low,10为high,43为a[low...high]的和
#endif

#if 1
#include <iostream>
#include <vector>
#define MIXINT 0x80000000

using namespace std;

struct SubArray{
	int low;
	int high;
	int sum;
	friend ostream& operator<<( ostream& out, const SubArray& sa );
};
// 重载友元
ostream& operator<<( ostream& out, const SubArray& sa )
{
	out << sa.low << "\t" << sa.high << "\t" << sa.sum;
	return out;
}

// A[low...high]的任何连续子数组A[i...j],该子数组拥有最大的和
SubArray find_max_subarray( int a[], int low, int high );

int main( void )
{
	cout << "2014-6-3 10:05:05" << endl;

	// 18为中点
	int a[] = { 13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7 };
	SubArray sa;
	sa = find_max_subarray( a, 0, sizeof( a ) / sizeof( int) - 1 );

	//printf( "%d\t%d\t%d\n", sa.low, sa.high, sa.sum );
	cout << sa << endl;

	return 0;
}

// 不是问题的子问题,故作为合并内容的一部分
// 子数组跨越中点,因此low <= i <= mid < j <= high
SubArray find_max_crossing_subarray( int a[], int low, int mid, int high )
{
	SubArray sa;
	int sum, left_sum, right_sum;

	// 求跨越中点子数组的左侧
	sum = 0;
	left_sum = MIXINT;
	for( int i = mid; i >= low; --i ){
		sum += a[i];
		if( sum > left_sum ){
			sa.low = i;
			left_sum = sum;
		}
	}

	// 求跨越中点子数组的右侧
	sum = 0;
	right_sum = MIXINT;
	for( int i = mid + 1; i <= high; ++i ){
		sum += a[i];
		if( sum > right_sum ){
			sa.high = i;
			right_sum = sum;
		}
	}

	sa.sum = left_sum + right_sum;

	return sa;
}

// 问题描述:公司股票收益,哪天买入,哪天卖出的收益最大化。
// 暴力解法:任取两天求差值,问题转换:得出相邻两天之间收益差值的数组,即求连续子数组的最大和值。
// 暴力解法:n天有n种日期C(n, 2) = O(n^2)-->分治法策略:O(nlgn)

// A[low...high]的任何连续子数组A[i...j]所处的位置必然是以下三种情况之一:
//① 完全位于子数组A[low ... mid]中,因此low <= i <= j <= mid
//② 完全位于子数组A[mid+1 ... high]中,因此mid < i <= j <= high
//③ 子数组跨越中点,因此low <= i <= mid < j <= high
SubArray find_max_subarray( int a[], int low, int high )
{
	SubArray sa, sa_l, sa_r, sa_c;

	if( low == high ){
		sa.low = low;
		sa.high = high;
		sa.sum = a[low];
		return sa;
	}

	int mid = ( low + high ) / 2;
	sa_l = find_max_subarray( a, low, mid );
	sa_r = find_max_subarray( a, mid + 1, high );
	sa_c = find_max_crossing_subarray( a, low, mid, high );

	if( sa_l.sum >= sa_r.sum && sa_l.sum >= sa_c.sum )
		return sa_l;
	else if( sa_r.sum >= sa_l.sum && sa_r.sum >= sa_c.sum )
		return sa_r;
	else
		return sa_c;
}
#endif

#if 0
采用动态规划方式:O(n)的时间复杂度解决
f(i) = pData[i]	if( i == 0 || f(i -1) <= 0 )
f(i) = f[i - 1] + pData[i]	if( i != 0 && f(i -1) > 0 )
#endif
SubArray find_max_subarray_D( int a[], int low, int high )
{
	//vector<SubArray> vecSA( high - low + 1 );
	SubArray saPre, saCur, saMax;
	saPre.sum = 0x80000000;
	saPre.low = -1;
	saPre.high = -1;
	saMax = saPre;

	for( int i = low; i <= high; ++i){
		if( saPre.sum <= 0 ){
			saCur.sum = a[i];
			saCur.low = i;
			saCur.high = i;
		}else{
			saCur.sum = saPre.sum + a[i];
			saCur.low = saPre.low;
			saCur.high = i;
		}

		saPre = saCur;
		if( saMax.sum < saCur.sum ){
			saMax = saCur;
		}
	}

	return saMax;
}

#endif






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值