问题:给定数组 a0,a1,a2...an ,求前一个元素减掉后一个元素的差组成的集合的最大值。
这个问题不是最大值最小值问题,因为不能确定最大值是不是在最小值前面。
设最终所求结果为 F。
1.先给出一种较易想到的方法。用动态规划方法。
设所求为 F ,现在假设与 ai 相关联的 F 是 Fi,能不能求出 Fi+1 呢?倘若可以有这样一个式子,便可在依次求 F 的过程中,保存最大的 F ,即是所求。深入思考,如果用 Fi 关联被减数(即 u - v 算式中的 u),那么,由于无法确定被减数是否已经被遍历过了(即,是否包含在 0 ~ i 个元素中),这不利于动态规划算式的展开。
动态规划实施过程中,应该让后一个待求量包含前面已求量的所有信息。
所以可以用 Fi 关联减数,这样可以保证被减数被遍历过,如此构建的动态规划状态转移方程有足够的信息。如图 1 给出一个状态转移图。
图1 由 i 到 (i + 1)状态转移
由上图知,Fi 转移到 Fi+1 ,设 u 是 0 ~ i - 1中的最大元素的下标,有两种可能,第一种是 Fi+1 = u 元素减去 i + 1 元素。第二种是一种特殊情况,i 元素比 u 元素大,Fi+1 = i 元素减去 i+1 元素。
这便是状态转移方程。如此,问题可解。
2.再给出分冶的解法。
如果现在给定了两个数组 A , B,为了求这两个数组拼接后的数组的 F,必须要知道哪些条件?显然,需要:
1).A 的 F 和 B 的 F,
2).A 中的最大值,B 中的最小值
实际上,1) 中的条件说明最终给出最大之差的两个元素都在 A 中,或者 B 中,通过对比 AF , BF 的大小确定哪一个是最终结果。而 2) 中的条件说明最终给出最大之差的两个元素,较大元素 max 在 A 中,较小元素 min 在 B 中,此时,max 必然是 A 中的最大值,min 必然是 B 中的最小值。
所以形成如下解法:
采用分冶的解法,将数组划分为左右两个数组,分别求出其 max,min ,F ,将这些值合并到合并数组的对应的值,则问题可解。
3.最后给出一种技巧性的解法。问题是求左边元素减右边元素的最大值,即求 :a0 - a1 , a1 - a2 ,a2 - a3,......这些值组成的集合 S 中,连续元素相加之和最大值。先证明这两个问题是等价的。
证明:如果 ai - aj 在考虑的范围内,由于 ai - aj = (ai - ai+1) + (ai+1 - ai+2) + ... + (aj-1 - aj),所以 ai - aj 可以被转换为求 S 中连续的从 i 到 j-1 元素的和。
由于每一个差都能转换为 S 集合的多个连续的元素的和,因此,考虑原数组差集合中的最大值,等价于 S 集合中多个连续元素的和的最大值。
证毕!
之前有一篇文章就是描述的这个问题:http://blog.youkuaiyun.com/lizhihaoweiwei/article/details/37739421
至此,问题得到解决。
最后给出这三种方法的解法的原代码。
//动态规划解法
int solutionDP(int *data,int nLength)
{
if(1 == nLength)
{
return *(data);
}
int max = *(data);
int curDiff = *(data) - *(data + 1);
int maxDiff = curDiff;
for (int i = 2;i < nLength;++ i)
{
if(max < *(data + i - 1))
{
max = *(data + i - 1);
}
curDiff = max - *(data + i);
if(maxDiff < curDiff)
{
maxDiff = curDiff;
}
}
return maxDiff;
}
//分冶解法
#define INFINITESIMAL -999999999;
int solutionDiv(int *data,const int startPos,const int endPos,int *max,int *min)
{
if(startPos == endPos)
{
*min = *max = *(data + startPos);
return INFINITESIMAL;
}
int mid = (startPos + endPos) / 2;
int leftMax,leftMin;
int leftMaxDiff = solutionDiv(data,startPos,mid,&leftMax,&leftMin);
int rightMax,rightMin;
int rightMaxDiff = solutionDiv(data,mid + 1,endPos,&rightMax,&rightMin);
*max = leftMax > rightMax ? leftMax : rightMax;
*min = rightMin < leftMin ? rightMin : leftMin;
int maxDiff = leftMaxDiff > rightMaxDiff ? leftMaxDiff : rightMaxDiff;
if(leftMax - rightMin > maxDiff)
{
maxDiff = leftMax - rightMin;
}
return maxDiff;
}
//转换为数组最大连续元素和解法
int solutionMaxSum(int *data,int nLength)
{
if(1 == nLength)
{
return *(data);
}
if(2 == nLength)
{
return *(data) - *(data + 1);
}
int *subs = new int[nLength - 1];
for (int i = 1;i < nLength;++ i)
{
*(subs + i - 1) = *(data + i - 1) - *(data + i);
}
int curDiff = 0,maxDiff = *(subs);
for (int i = 0;i < nLength - 1;++ i)
{
maxDiff = curDiff > maxDiff ? curDiff : maxDiff;
curDiff += *(subs + i);
if(0 > curDiff)
{
curDiff = 0;
}
}
return maxDiff;
}