我试图解决的问题已经在2007年的USACO比赛中给出了。简而言之,你得到了一个整数数组,并允许交换连续元素(A [i],A [i + 1] ])。在最小数量的交换操作之后,数组必须按升序排序,或者是其排序版本的两个连续部分的串联。 一个例子:
1, 2, 3, 4
3, 4, 1, 2
4, 1, 2, 3
问题需要在O(N)或O(log(N))时间内解决;任何事情都会太慢。此特定问题没有评论或源代码。我已经提到了索引树和反转,但是我无法绕过相关性,因为我只允许对连续项执行交换操作。 可以找到问题的陈述here。
答案 0 :(得分:3)
排列相对于另一个排列的反转次数是将一个变换为另一个所需的相邻互换的最小数量。一些抽象代数表明我们对找到输入数组的旋转感兴趣,这种旋转最小化了相对于排序顺序的反转次数。
有多种算法可用于计算反转。这里有用的一个使用二进制索引树来使下面的伪代码快速运行(O(n log n))。
Let P[1], ..., P[n] be the input permutation on 1, ..., n
For k = 1, ..., n
Initialize A[k] = 0
End For
For j = 1, ..., n
I[P[j]] = A[j] # Number of elements before and greater than P[j]
For k = 1, ..., P[j] - 1 # Replace this loop with an O(log n) tree operation
A[k] = A[k] + 1
End For
End For
The total number of inversions is I[1] + ... + I[n]
现在,如果我们将一个元素j
从数组末尾旋转到开头,我们会更新
For k = 1, ..., j - 1 # Replace this loop with an O(log n) tree operation
I[k] = I[k] + 1
End For
I[j] = 0
并相应地在恒定时间内重新计算总和。重复适当的次数并采取最低限度。
答案 1 :(得分:1)
根据Wikipedia article基于比较的排序,O(n log n)
的渐近最坏情况复杂度是最优的,这使得算法即使没有仅交换邻近元素的约束也是不可能的。要在给定约束条件下看到O(log n)
的边界是不可能的,请考虑实例类
n,n-1,n-2,...,1
表示任何非负整数n
,即以完全错误的方式排序的列表。至少n-1
次交换是将1
移动到第一个位置所必需的,这不能在O(log n)
时间内完成。此外,即使是按所需顺序排序的列表也必须被读取以确定它是,它还需要至少n-1
次比较,从而使O(log n)
的最坏情况界限不可能。
答案 2 :(得分:1)
使用二进制索引树(又名Fenwik树),我们可以在O(log n)
时间内添加元素并计算它们的前缀和。使用它解决问题的一种方法是:
(1)计算置换的反演向量:遍历数组,对于每个索引i
和元素v
,首先将(i - prefix sum 0 to v)
添加到结果中,然后在索引{{ 1}}在树上。如果你仔细想想,如果所有前面的元素都大于v
,i
等于我们计算i
的倒数的数量;所以我们减去到目前为止遇到的小于i
的元素数,这正是到目前为止树中索引v
的前缀总和。此操作需要时间v
例如,
O(n * log n)
(2)现在有趣的部分。由于允许我们旋转排序顺序,让我们想一想如果我们将目标排序顺序中的Array: {1,5,4,6,2,0,3}
Traversal: (0-0)
(1-1)
(2-1)
...
Inversion vector: 0 0 1 0 3 5 3
移动到开头会发生什么:而不是零,我们数组中的6
现在会计数3次倒数,一个等于前面元素数量的数字;并且6
之后的每个元素都会减少一次,因为它不再需要与6
交换。继续:
6
但是如果我们只是在数组中使用元素的位置,那么这个操作可能需要时间Array: {1,5,4,6,2,0,3}
Original vector: 0 0 1 0 3 5 3 (12)
V with 6 at start: 0,0,1,3,2,4,2 (12)
V with 5,6 at start: 0,1,0,2,1,3,1 (8)
V with 4,5,6 at start: 0,1,2,1,0,2,0 (6)
(3,4,5,6,0,1,2) 0,1,2,1,0,2,6 (12)
(2,3,4,5,6,0,1) 0,1,2,1,4,1,5 (14)
(1,2,3,4,5,6,0) 0,0,1,0,3,0,4 (8)
,我们可以预先哈希。给定原始反演向量O(n)
,我们重复将以下操作应用于其0 0 1 0 3 5 3
的总和并选择最小值:
12
正如我们所看到的,如果我们选择轮换目标订单(4,5,6,0,1,2,3),则所需的最小交换为6:
12
+ 3 - 3 (=12) // + index of 6 - (n - index of 6 - 1)
+ 1 - 5 (=8) // + index of 5 - (n - index of 5 - 1)
+ 2 - 4 (=6) // ...
+ 6 - 0 (=12)
+ 4 - 2 (=14)
+ 0 - 6 (=8)