找到将数组转换为算术级数的最低成本

时间:2015-07-31 16:24:15

标签: algorithm

我最近在一次采访中遇到了这个问题。我无法想出一个算法。

给定一个未排序的整数数组,我们必须找到将此数组转换为算术级数的最低成本,如果在数组中更改了任何元素,则会产生1个单位的成本。此外,元素的值介于(-inf,inf)。

之间

我有点意识到DP可以在这里使用,但我无法解决这个问题。价值观受到一些限制,但我不记得了。我只是在寻找高级伪代码。

5 个答案:

答案 0 :(得分:2)

修改 不幸的是,这是一个正确的解决方案,虽然简单易懂,但在O(n ^ 3)处效率不高。

function costAP(arr) {
    if(arr.length < 3) { return 0; }
    var minCost = arr.length;
    for(var i = 0; i < arr.length - 1; i++) {
        for(var j = i + 1; j < arr.length; j++) {
            var delta = (arr[j] - arr[i]) / (j - i);
            var cost = 0;
            for(var k = 0; k < arr.length; k++) {
                if(k == i) { continue; }
                if((arr[k] + delta * (i - k)) != arr[i]) { cost++; }
            }
            if(cost < minCost) { minCost = cost; }
        }
    }
    return minCost;
}
  • 在数组
  • 中的每个不同索引对之间查找相对 delta
  • 使用相对增量来测试使用 delta
  • 将整个阵列转换为AP的成本
  • 返回最低费用

答案 1 :(得分:2)

Louis Ricci有正确的基本想法,即寻找最大的现有算术级数,但假设它必须出现在一次运行中,而实际上这个级数的元素可以出现在职位,例如:

1 42 3 69 5 1111 2222 8

只需要4次更改:

  42   69   1111 2222
1    3    5           8

要计算这一点,请注意每个AP都有一个最右边的元素。我们可以假设输入向量的每个元素i依次是最右边的AP位置,并且对于每个这样的i考虑i的左边的所有位置j,确定每个(i,j)组合隐含的步长,并且当这是整数(表示有效的AP),在暗示此步长的元素数量上加1,在位置i结束 - 因为所有这些元素都属于同一个AP。总体最大值是最长的AP:

struct solution {
    int len;
    int pos;
    int step;
};

solution longestArithProg(vector<int> const& v) {
    solution best = { -1, 0, 0 };

    for (int i = 1; i < v.size(); ++i) {
        unordered_map<int, int> bestForStep;
        for (int j = 0; j < i; ++j) {
            int step = (v[i] - v[j]) / (i - j);
            if (step * (i - j) == v[i] - v[j]) {
                // This j gives an integer step size: record that j lies on this AP
                int len = ++bestForStep[step];
                if (len > best.len) {
                    best.len = len;
                    best.pos = i;
                    best.step = step;
                }
            }
        }
    }

    ++best.len;     // We never counted the final element in the AP
    return best;
}

上面的C ++代码使用O(n ^ 2)时间和O(n)空间,因为它遍历每对位置i和j,为每个位置执行单个哈希读写。回答原始问题:

int howManyChangesNeeded(vector<int> const& v) {
    return v.size() - longestArithProg(v).len;
}

答案 2 :(得分:0)

给定未排序整数的数组a = [a_1, a_2, ..., a_n],让diffs = [a_2-a_1, a_3-a_2, ..., a_n-a_(n-1)]

diffs中找出最大值,并调整所需的a中的所有值,以便所有相邻值都相差这一数量。

答案 3 :(得分:0)

有趣的是,即使我今天在我的校园招聘测试中也遇到了同样的问题。在进行测试时,我意识到基于阵列中2个后续元素之间最常见的差异来改变元素的逻辑在某些情况下会失败。 例如-4,5,8,9。根据如上所述的a2-a1,a3-a2的逻辑,回答shud为1,而不是这种情况。 正如你所建议的那样,我觉得它可以为数组成本中的每个元素考虑2个值,当它被修改时以及未被修改时返回最小值2.当你到达结束时最终终止数组。

答案 4 :(得分:0)

这个问题有一个简单的几何解释,它表明它可以在O(n ^ 2)时间内解决,并且可能无法以更快的速度求解(从3SUM减少)。假设我们的数组是[1, 2, 10, 3, 5]。我们可以将该数组写为一系列点

(0,1), (1,2), (2,10), (3,3), (4,5)

其中x值是数组项的索引,y值是数组项的值。现在的问题是找到一条通过该组中最大可能点数的线。转换数组的成本是不在一条线上的点数,当一条线上的点数最大化时,这个点被最小化。

在此SO帖子中给出了对该问题的一个相当明确的答案:What is the most efficient algorithm to find a straight line that goes through most points?

这个想法:对于从左到右的集合中的每个点P,找到通过该点的线和P​​右边的最大点数(我们不需要看左边的点) P因为它们会在早期的迭代中被捕获。)

为了找到P右侧的P共线点的最大数量,对于每个这样的点,Q计算线段PQ的斜率。在哈希映射中计算不同的斜率。映射到最大命中数的斜率是您正在寻找的。

技术问题:您可能不希望使用浮点算法来计算斜率。另一方面,如果使用有理数,则可能必须计算最大公约数,以便通过比较分子和分母来比较分数,后者将运行时间乘以因子log n。相反,您应该通过测试a/b来检查有理数c/dad == bc的相等性。

上面引用的SO帖子给出了3SUM的减少,即这个问题是3SUM-hard,这表明如果这个问题可以比O(n ^ 2)快得多地解决,那么3SUM也可能是解决得比O(n ^ 2)快得多。这是整数所在的条件(-inf,inf)的来源。如果已知整数来自有界集,则3SUM的减少不是确定的。

一个有趣的进一步问题是,当整数在有界集(-N,N)中时,维基百科中用于在O(n + N log N)时间内求解3SUM的想法是否可用于解决最小成本问题。将数组转换为AP问题的时间比O(n ^ 2)快。