查找数组中的哪两个值最大化给定表达式?

时间:2014-09-01 10:50:27

标签: c++ algorithm

我遇到了一个非常简单的面试问题,但我的解决方案不正确。对此有何帮助? 1)我的解决方案中的任何错误? 2)时间复杂度O(n)的任何好主意?

问题:

给定一个int数组A[],定义X=A[i]+A[j]+(j-i), j>=i。找到X的最大值?

我的解决方案是:

int solution(vector<int> &A){
    if(A.empty())
        return -1;
    long long max_dis=-2000000000, cur_dis;
    int size = A.size();
    for(int i=0;i<size;i++){
        for(int j=i;j<size;j++){
            cur_dis=A[j]+A[i]+(j-i);
            if(cur_dis > max_dis)
                max_dis=cur_dis;
        }
    }
    return max_dis;
}

3 个答案:

答案 0 :(得分:6)

关键的见解是,只有在您确定它们是否可以证明可用之前,您才能在O(n)中跟踪可能有用的值。

从best_i = best_j = max_i = 0开始。前两个跟踪要在解决方案中使用的i和j值。下一个将记录具有i的最高贡献因子的索引,即A[i] - i最高的那个。

让我们为i和j&#34; X i,j &#34;的某些值调用X的值,并从记录我们迄今为止的最佳解决方案开始ala X best = X 0,0

沿阵列增加n ......

  • 只要[n]的价值提供更好的&#34; i&#34;对A[i] - i的贡献比max_i更新,更新max_i。

  • 每当使用n作为&#34; j&#34; index产生X max_i,n 大于X best ,best_i = max_i,best_j = n。

讨论 - 为什么/如何运作

j_random_hacker的评论建议我草拟一个证明,但说实话,我不知道从哪里开始。我会尽力解释 - 如果其他人有更好的解释,请填写....

重述问题:最大X i,j 其中j> = i。鉴于我们可以设置X 0,0 的初始X best ,问题是知道何时更新它以及什么。当我们考虑数组中的连续索引作为j的潜在值时,我们想要为某些i生成X i,j = n (下面讨论)以与X best 进行比较。但是,我有什么用?那么,给定从0到n的任何索引是&lt; = j,如果我们从已经访问过的索引中选择最佳的i值,则j> = i约束是不相关的。我们通过将与i相关的贡献与j相关的贡献 - A[i] - i分开来计算出最佳的i值 - 所以为了准备考虑我们是否有一个j = n的新的最佳解决方案我们必须我们去的时候也要保持best_i变量。

解决问题的方法

无论它的价值如何 - 当我在寻找解决方案的时候,我在纸上写下了一些想象中的i和j贡献,我可以看到它涵盖了有趣的案例......其中Ci和Cj是贡献与n分别用作i和j相关,类似于

n    0  1 2  3 4
Ci   4  2 8  3 1
Cj   12 4 3  5 9

你会注意到我没有费心去挑选Ci可能是A [i] - 我在Cj是A [j] + j的时候......我可以看到新兴的解决方案适用于任何公式,这将使得捕获有趣的案例变得更加困难。那么 - 有趣的案例是什么?当n = 2时,Ci值高于我们在之前的元素中看到的任何值,但只考虑那些早期元素的知识,我们还无法看到使用它的方法。这种情况是单一的&#34;伟大&#34;问题的复杂性。需要的是Cj值至少为9,因此Xbest得到改善,恰好在n = 4时出现。如果我们在[3]发现了更好的Ci,那么我们就可以了当然想用它。 best_i跟踪等待足够好的Cj值索引的位置。

答案 1 :(得分:2)

我的评论的更长版本:如何从两端迭代数组,尝试找到最高数字,同时减少距离appripriate端的距离。那会找到正确的索引(因而是正确的X)吗?

#include <vector>
#include <algorithm>
#include <iostream>
#include <random>
#include <climits>

long long brutal(const std::vector<int>& a) {
    long long x = LLONG_MIN;
    for(int i=0; i < a.size(); i++)
        for(int j=i; j < a.size(); j++)
            x = std::max(x, (long long)a[i] + a[j] + j-i);
    return x;
}

long long smart(const std::vector<int>& a) {
    if(a.size() == 0) return LLONG_MIN;
    long long x = LLONG_MIN, y = x;
    for(int i = 0; i < a.size(); i++)
        x = std::max(x, (long long)a[i]-i);
    for(int j = 0; j < a.size(); j++)
        y = std::max(y, (long long)a[j]+j);
    return x + y;
}

int main() {
    std::random_device rd;
    std::uniform_int_distribution<int> rlen(0, 1000);
    std::uniform_int_distribution<int> rnum(INT_MIN,INT_MAX);
    std::vector<int> v;
    for(int loop = 0; loop < 10000; loop++) {
        v.resize(rlen(rd));
        for(int i = 0; i < v.size(); i++)
            v[i] = rnum(rd);
        if(brutal(v) != smart(v)) {
            std::cout << "bad" << std::endl;
            return -1;
        }
    }
    std::cout << "good" << std::endl;
}

答案 2 :(得分:0)

我会用伪代码写,因为我没有太多时间,但这应该是使用递归的最佳表现方式

compare(array, left, right)

val = array[left] + array[right] + (right - left);

if (right - left) > 1
  val1 = compare(array, left, right-1);
  val2 = compare(array, left+1, right);

  val = Max(Max(val1,val2),val);
end if

return val

而不是简单地打电话

compare(array,0,array.length);

我认为我找到了一个非常快的解决方案,但您需要检查它:

你需要重写你的数组,如下所示

Array[i] = array[i] + (MOD((array.lenght / 2) - i));

然后你只需找到数组的2个最高值并求它们,这应该是你的解决方案,差不多是O(n)

等等,也许我错过了什么......我得检查一下。

好的,你从这个新数组得到2个最高值,并保存位置i和j。然后你需要从原始数组计算你的结果。

------------编辑

这应该是我测试过的Tony D(在c#中)建议的方法的实现。

        int best_i, best_j, max_i, currentMax;

        best_i = 0;
        best_j = 0;
        max_i = 0;
        currentMax = 0;

        for (int n = 0; n < array.Count; n++)
        {
            if (array[n] - n > array[max_i] - max_i) max_i = n;

            if (array[n] + array[max_i] - (n - max_i) > currentMax)
            {
                best_i = max_i;
                best_j = n;
                currentMax = array[n] + array[max_i] - (n - max_i);
            }

        }

        return currentMax;