本文题目在《算法导论》第38页(主要是写题目好麻烦)
通过观察图4-1,我们会发现:要想使收益最大化,那么就要选择价格变化最大的两天。而所谓的价格变化,实际上是两者之间差的绝对值得变化。
因此我们可以化简这个问题(这一步很重要,它反映了问题的本质,从而解决一类问题)
所以,图4-1就可以简化为 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (天数)
13 -3 -25 20 -3 -16 -23 18 20 -7 12 -5 -22 15 -4 7 (价格变化)
此时你可以看到:应该在第8天买入,第11天卖出。
要想写出一个好的算法,首先要做的是从所给的例子中发现规律(就跟数学证明一样)
在这个题中,我们可以看出。要想做到收益最大化。应该满足下面几个条件:
1、这个非空连续子数组中,每个数相加,所得的总和应该是全部数组中最大的。
2、在这个数组中,负数实际上对总和没用(起作用的是非零的正整数)。
3、这个数组的开头和结尾的位置都应该为正整数。
4、这个非空连续子数组有三种范围(数组A【low,mid]): low<=i<=j<=mid、low<i<=j<=mid、low<i<=j<mid //这也是缩小范围的一种方式,总之区分过程越简单越好
//在这里,犯了一个错误,想把查找范围缩小,以为可以通过减小范围来减少运算量,从每个正整数开始,到下一个正整数为止(实际上,这个划分范围就比较麻烦了,反而增大了编程过程)
——————————————————————————————————————这个代码是大神写的(我们不生产代码,我们只是代码的搬运工)
/*
最优方法,时间复杂度O(n)
和最大的子序列的第一个元素肯定是正数
因为元素有正有负,因此子序列的最大和一定大于0
*/
int MaxSubSum3(int *arr,int len)
{
int i;
int MaxSum = 0;
int CurSum = 0;
for(i=0;i<len;i++)
{
CurSum += arr[i];
if(CurSum > MaxSum)
MaxSum = CurSum;
//如果累加和出现小于0的情况,
//则和最大的子序列肯定不可能包含前面的元素,
//这时将累加和置0,从下个元素重新开始累加
if(CurSum < 0)
CurSum = 0;
}
return MaxSum;
}
上面的这个代码仔细想了想,发现它把整个数组分成三部分(有些情况下是两部分) //这才是减小范围,从而减小运算量
把整个数组里的元素加一遍,通过找出最大的连续数组前面的不符合条件方法,确定出最后结果。