4

The problem i'm trying to solve has been given on a USACO competition in 2007. In a nutshell, you are given an array of integers and are allowed to swap consecutive elements (A[i], A[i+1]). After a minimal number of swap operations the array must either be sorted in ascending order or be the concatenation of two consecutive parts of its sorted version. An Example:

1, 2, 3, 4
3, 4, 1, 2
4, 1, 2, 3

The problem needs to be solved in O(N) or O(log(N)) time; anything more would be too slow. There are no comments or source code for this particular problem. Index trees and inversions have been mentioned to me, but I couldn't wrap my head around the correlation, since I'm only allowed to perform swap operations on consecutive items. The statement for the problem can be found here.

5
  • Sorting even without such constraints is O(nlogn); how can this algorithm be O(n) or O(logn)? Even if you need to sort just the two halves of the array, it should be in the same ballpark as O(nlogn), or am I missing something? Maybe you meant that there should only be O(n) swaps, but that it may take more time to determine what elements to swap? Commented Nov 25, 2015 at 10:32
  • Also, would the array [1,3,5,2,4] be a solution, too? It consists of two sorted parts, [1,3,5] and [2,4], but from the link I think this is not legal, right? Commented Nov 25, 2015 at 10:38
  • The limits given are N<=100,000 and a time limit of 0.2 sec. It has to be faster than O(N^2). Commented Nov 25, 2015 at 10:38
  • 1, 3, 5, 2, 4 is not a solution. "FJ thinks cow i may stand only to the left of cow i+1 (for all i, 1 <= i <= N-1) and that cow N may only stand to the left of Cow 1." Commented Nov 25, 2015 at 10:39
  • Note that "faster than O(n²)" is far from the same as "O(n) or O(logn)". I'd assume that there is no real cutoff and that a O(nlogn) algorithms or similar is sufficient. Please verify and update your question accordingly. Also, have you tried anything? Commented Nov 25, 2015 at 10:52

3 Answers 3

4

The number of inversions of a permutation relative to another permutation is the minimum number of adjacent swaps required to transform one into the other. A little bit of abstract algebra shows that we're interested in finding the rotation of the input array that minimizes the number of inversions relative to sorted order.

There are several algorithms for counting inversions. The one that's useful here uses a binary-indexed tree to make the following pseudocode run quickly (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]

Now, if we rotate one element j from the end of the array to the beginning, we update

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

and recompute the sum in constant time accordingly. Repeat the appropriate number of times and take the minimum.

Sign up to request clarification or add additional context in comments.

Comments

1

According to the Wikipedia article on comparison-based sorting, an asymptotic worst-case complexity of O(n log n) is optimal, which would make an algorithm even without the constraints of swapping only adjacent elements impossible. To see that a bound of O(log n) is impossible under the given constraints, consider the class of instances

n,n-1,n-2,...,1

for any nonnegative integer n, i.e. the lists which are sorted in exactly the wrong way. At least n-1 swaps are necessary to move the 1 to the first position, which cannot be done int O(log n) time. Furthermore, even a list which is sorted in the desired order has to be read to decide that it is, which also needs at least n-1 comparisons, rendering a worst-case bound of O(log n) impossible.

Comments

1

With a binary indexed tree (aka Fenwik tree), we can add elements and calculate their prefix sums in O(log n) time. One way to solve your problem using it is the following:

(1) Calculate the permutation's inversion vector: traverse the array and for each index i and element v, first add (i - prefix sum 0 to v) to the results, then add 1 at index v in the tree. If you think about it, i equals the number of inversions we would count for i if all preceding elements were greater than i; so we subtract the number of elements less than v encountered so far, which is exactly the prefix sum up to index v in the tree so far. This operation takes time O(n * log n) For example,

Array:           {1,5,4,6,2,0,3}

Traversal:      (0-0)
                  (1-1)
                    (2-1)
                      ...

Inversion vector: 0 0 1 0 3 5 3

(2) Now the fun part. Since we are allowed to rotate the sorted order, let's think what would happen if we moved the 6 in the target-sorted-order to the beginning: instead of zero, the 6 in our array would now count 3 inversions, a number equal to the count of elements preceding it; and each element following the 6 will have one inversion less since it would no longer need to be swapped with 6. Continue:

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)

But this operation can take time O(n) if we simply use the elements' position in the array, which we can hash beforehand. Given original inversion vector 0 0 1 0 3 5 3, we repeatedly apply the following operation to its sum of 12 and choose the minimum:

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)

As we can see, the minimum swaps needed are 6 if we choose rotated target order (4,5,6,0,1,2,3):

Array:    {1,5,4,6,2,0,3}
Target:   {4,5,6,0,1,2,3}
# Swaps:   0 1 2 1 0 2 0

Swaps:             0,2
           5,1
             4,1
           4,5
               6,1
                 0,1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.