【问题标题】:Divide and Conquer to find maximum difference in an array分而治之以找到数组中的最大差异
【发布时间】:2017-03-23 00:50:23
【问题描述】:

我正在尝试解决给定数组的问题,我需要计算最大差异,以便较大的元素出现在较小的元素之后。

这是一个更好的问题陈述

给定n 天的每一天的股票价格,一个人通过仅做一笔交易可以获得的最大利润是多少。一笔交易意味着该人可以在一天内只购买一只股票并在以后出售。

我正在尝试使用分而治之的算法来解决这个问题。

在我的递归函数中,我试图将数组分成两半,但我不确定如何进行逻辑。我只是得到每一半的最大差异并进行比较吗?

int calculateMaxDiff(int *src, int start, int end){
    if(end - start == 1) return src[start];

    int middle = (start + end)/ 2;
    int half1_diff;
    int half2_diff;
    half1_diff = calculateMaxDiff(src, start, middle);
    half2_diff = calculateMaxDiff(src, middle, end);

    //Do i need to have two loops here that calculate the diffs for each halves
    .... 
    return max(half1_diff, half2_diff);
 }

编辑:示例输出

给出一个数组 {12, 9, 18, 3, 7, 11, 6, 15, 6, 1 ,10} 应该返回 12 作为 15 和 3 之间差异的结果

【问题讨论】:

  • 这到底是为了达到什么目的?您有示例使用案例和预期结果吗?
  • @tadman 我添加了一个示例输出
  • 因此您需要计算整个数组的最大 最小值。这里不需要递归,只需要两个变量和一个迭代循环。事实上,我认为你不能(有效地)用递归解决这个问题。
  • 是的,我知道,但我正在尝试通过分而治之来实现这一目标
  • 我知道你在尝试什么,但我认为这不仅没有意义,而且只有一个返回值是不可能的。

标签: c++ algorithm


【解决方案1】:

你的问题中的问题可以翻译成更好的问题陈述:

给定 n 天内每天的股票价格,一个人仅进行一次交易可以赚取的最大利润是多少。一笔交易意味着该人可以在一天内只购买一只股票并在以后出售。

分而治之的解决方案:让我们看看我们是否可以通过将输入分成两半,解决每个子数组中的问题,然后将两者组合在一起来解决这个问题。事实证明,我们实际上可以做到这一点,并且可以有效地做到这一点!直觉如下。如果我们只有一天,最好的选择是在当天买入,然后在当天卖回,不赚钱。否则,将数组分成两半。如果我们考虑最佳答案可能是什么,它必须位于以下三个位置之一:

  1. 正确的买入/卖出对完全发生在上半年。
  2. 正确的买入/卖出对完全发生在下半年。
  3. 正确的买入/卖出对出现在两半 - 我们在上半年买入,然后在下半年卖出。

我们可以通过在前半部分和后半部分递归调用我们的算法来获得 (1) 和 (2) 的值。对于方案(3),获得最高利润的方法是在上半年的最低点买入,在下半年的最高点卖出。我们可以通过对输入进行简单的线性扫描并找到两个值来找到两半中的最小值和最大值。这给了我们一个具有以下递归的算法:

T(n) = 2T(n/2) + O(n)

T(n) = O(nlogn)

这是一个简单的 Python 实现。它非常易于理解,也很容易转换为 C++:

def DivideAndConquerSingleSellProfit(arr):
    # Base case: If the array has zero or one elements in it, the maximum
    # profit is 0.
    if len(arr) <= 1:
        return 0;

    # Cut the array into two roughly equal pieces.
    left  = arr[ : len(arr) / 2]
    right = arr[len(arr) / 2 : ]

    # Find the values for buying and selling purely in the left or purely in
    # the right.
    leftBest  = DivideAndConquerSingleSellProfit(left)
    rightBest = DivideAndConquerSingleSellProfit(right)

    # Compute the best profit for buying in the left and selling in the right.
    crossBest = max(right) - min(left)

    # Return the best of the three
    return max(leftBest, rightBest, crossBest)

编辑:这是上述算法的 C++ 实现

#include <iostream>
#include <algorithm>
using namespace std;
int calculateMin(int a[], int low, int high)
{
    int i,mini;
    mini = a[low];
    for(i=low;i<=high;i++)
    {
        if(a[i]<mini)
        {
            mini = a[i];
        }
    }
    return mini;
}
int calculateMax(int a[], int low, int high)
{
    int i,maxi;
    maxi = a[low];
    for(i=low;i<=high;i++)
    {
        if(a[i]>maxi)
        {
            maxi = a[i];
        }
    }
    return maxi;
}
int calculateMaxDiff(int a[], int low, int high)
{
    if(low>=high)
        return 0;

    int mid = (low+high)/2;
    int leftPartition = calculateMaxDiff(a,low,mid);
    int rightPartition = calculateMaxDiff(a,mid+1,high);
    int left = calculateMin(a,low,mid); // calculate the min value in the left partition
    int right = calculateMax(a,mid+1,high); // calculate the max value in the right partition
    return max(max(leftPartition, rightPartition), (right - left));

}
int main() {
    int arr[] = {12, 9, 18, 3, 7, 11, 6, 15, 6, 1 ,10};
    int len = sizeof(arr)/sizeof(arr[0]);
    int ans = calculateMaxDiff(arr, 0, len-1);
    cout << "Maximum Profit: " <<ans<<endl;
    return 0;
}

希望对你有帮助!!!

【讨论】:

  • 无意冒犯,但是当存在线性(并且至少同样简单)版本时,我发现将此 O(nlgn) 算法称为“高效”有点过分;)
  • 感谢 @User_Targaryen 的故障
  • @PatriceGahide:作者试图对这个问题实施分而治之的技术。这就是为什么我提供了分而治之的方法。连我都知道这个问题有一个简单的线性解决方案!!!
  • 我完全理解,这确实很好地回答了这个问题。只是你说这个 O(nlgn) 算法是“高效的”,甚至没有提到一个简单的遍历算法渐近地比你在这里提供的解决方案更好的事实,或者(更重要的是)D&C 也可以 只需少量调整即可实现线性时间复杂度。考虑到这一点,“高效”这个词似乎有点乐观,仅此而已。没什么大不了的。
【解决方案2】:

不需要复杂的 D/C 算法,因为简单的循环和检查类似

 maxdiff = max(current - min_so_far, maxdiff)
 update min_so_far

解决问题

如果你真的想应用分治法,你可以从递归函数返回三元组{local_min, local_max, local_max_diff}

left = calculateMaxDiff(start, middle)
right = calculateMaxDiff(middle + 1, end)
return {min(left.local_min, right.local_min), 
        max(left.local_max, right.local_max), 
        max(left.local_diff, right.local_diff, right.localmax - left.local_min)

【讨论】:

  • 应该是 maxdiff = max(current - min_so_far, maxdiff)
【解决方案3】:

分治算法的关键是征服部分。

对于这个问题,最重要的条件是:

较大的元素出现在较小的元素之后

对于数组src,将src分成两半,half1half2,假设答案在ij的位置,现在有3种情况:

  1. ij 都在 half1 -> half1_diff
  2. ij 都在 half2 -> half2_diff
  3. ihalf1jhalf2

所以主要是处理case3。由于后面是较大的,所以我们只需要在half1中找到最小值min_half1和在half2中找到最大值max_half2,并检查它是否满足条件max_half2 &gt;= min_half1并将结果更新为max(half1_diff, half2_diff, max_half2-min_half1).

为了有效地计算min_half1max_half2,你必须保留数组中minmax值的记录,这需要O(1)时间。

所以T(n) = 2T(n/2) + O(1)T(n) = O(n)


查看示例了解更多详情

http://ideone.com/TbIL2r

【讨论】:

    猜你喜欢
    • 2018-03-10
    • 2014-07-26
    • 2013-01-16
    • 2017-06-07
    • 2015-12-31
    • 2016-05-01
    • 2014-03-24
    • 2017-01-12
    • 2020-01-07
    相关资源
    最近更新 更多