【问题标题】:Maximize number of inversion count in array最大化数组中的反转计数
【发布时间】:2018-07-09 13:36:52
【问题描述】:

我们得到一个未排序的整数数组A(允许重复),其大小N 可能很大。我们可以统计索引为i < j的对的数量,其中A[i] < A[j],我们称之为X

我们可以从数组中更改最大一个元素,其成本等于绝对值的差值(例如,如果我们将索引k 上的元素替换为新数字K,则成本Y 是@ 987654329@)。 我们只能用数组中的其他元素替换这个元素。

我们希望找到X + Y 的最小可能值。

一些例子:

  • [1,2,2] 应返回 1(将 1 更改为 2,使数组变为 [2,2,2])
  • [2,2,3] 应该返回 1(将 3 改为 2)
  • [2,1,1] 应该返回 0(因为不需要更改)
  • [1,2,3,4] 应该返回 6(这已经是可能的最小值)
  • [4,4,5,5] 应该返回 3(这可以通过将前 4 更改为 5 或将后 5 更改为 4 来实现)

可以通过简单的O(n²) 解决方案找到对的数量,在 Python 中:

def calc_x(arr):
    n = len(arr)
    cnt = 0
    for i in range(n):
        for j in range(i+1, n):
            if arr[j] > arr[i]:
                cnt += 1    
    return cnt

蛮力解决方案很容易写成例如:

def f(arr):
    best_val = calc_x(arr)
    used = set(arr)
    for i, v in enumerate(arr):
        for replacement in used:
            if replacement == v:
                continue

            arr2 = arr[0:i] + replacement + arr[i:]

            y = abs(replacement - v)
            x = calc_x(arr2)

            best_val = min(best_val, x + y)
    return best_val

我们可以在O(n*log(n)) 中为每个元素计算其右侧大于自身的项目数,例如使用 AVL 树或合并排序的一些变体。 但是,我们仍然需要寻找要更改的元素以及可以实现哪些改进。

这是作为一个面试问题给出的,我想要一些关于如何有效解决这个问题(数据结构或算法)的提示或见解。

【问题讨论】:

    标签: arrays algorithm data-structures


    【解决方案1】:

    在计算反转时,一定要使用 O(n log n) 复杂度。

    我们可以看到,当您更改索引 k 处的值时,您可以:

    1) 增加它,然后可能减少元素大于 k 的反演次数,但增加元素小于 k 的反演次数

    2) 减少它(发生相反的事情)

    让我们尽量不要在每次更改值时都计算 x。你需要知道什么?

    情况1):

    您必须知道左侧有多少元素小于您的新值 v,右侧有多少元素大于您的值。你可以很容易地在 O (n) 中检查它。那么你的x现在是什么?你可以用下面的公式来计算:

    prev_val - your previous value
    prev_x - x that you've counted at the beginning of your program
    prev_l - number of elements on the left smaller than prev_val
    prev_r - number of elements on the right bigger than prev_val
    v - new value
    l - number of elements on the right smaller than v
    r - number of elements on the right bigger than v
    
    new_x = prev_x + r + l - prev_l - prev_r
    

    在第二种情况下,您几乎会做相反的事情。

    现在你得到类似 O(n^3) 而不是 O(n^3 log n) 的东西,这可能仍然很糟糕。不幸的是,这就是我现在想出的全部。如果我想出更好的东西,我一定会告诉你的。

    编辑:内存限制呢?有没有?如果没有,您可以只为数组中的每个元素制作两组,其中包含当前元素之前和之后的元素。然后你可以在O(log n)中找到更小/更大的数量,使你的时间复杂度为O(n^2 log n)。

    编辑 2:我们还可以尝试检查,对于每个可能的值 v,哪个元素最适合更改为值 v。您可以在检查每个元素的同时制作两组并从中添加/删除元素,使时间复杂度为 O(n^2 log n) 而不使用太多空间。所以算法是:

    1) 确定可以改变任何元素的每个值 v,计算 x

    2) 对于每个可能的值 v:

    • 制作两组,将所有元素推入第二组
    • 对于数组中的每个元素 e: 将前一个元素(如果有的话)添加到第一个集合中并从第二个集合中删除元素 e,然后计算集合 1 和 2 中较大/较小元素的数量并计算新的 x

    编辑 3:您可以使用前缀 sum 作为值,而不是制作两组。已经是 O (n^2),但我认为我们可以做得更好。

    【讨论】:

      猜你喜欢
      • 2011-08-26
      • 2020-07-08
      • 2021-09-04
      • 1970-01-01
      • 2010-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多