Open In App

How to evenly put N objects into N places with adjacent moves

Improve
Improve
Like Article
Like
Save
Share
Report

Suppose you have N objects distributed unevenly in N containers. You can move one object from a container to an adjacent container, it is considered to be one move. What is the strategy that minimizes the number of moves to keep the time complexity O(N)? 
Examples:
 

Example 1. Suppose you have 5 containers that are filled as: 
1, 2, 1, 1, 0 
You will need three moves to pass one object from the second container to the last one ->, move from 2nd to the third first, then to the fourth and finally to the fifth: 
1, 1, 2, 1, 0 
1, 1, 1, 2, 0 
1, 1, 1, 1, 1
Example 2. Now a bit more complicated one. 
Suppose you have 5 containers and all objects are in the last container: 
0, 0, 0, 0, 5 
Obviously, you will move from right to the left: 
0, 0, 0, 1, 4 
0, 0, 1, 0, 4 
0, 1, 0, 0, 4 
1, 0, 0, 0, 4 
1, 0, 0, 1, 3 
1, 0, 1, 0, 3 
1, 1, 0, 0, 3 
1, 1, 0, 1, 2 
1, 1, 1, 0, 2 
1, 1, 1, 1, 1 
It takes 10 moves 
Example 3. All objects are in the middle: 
0, 0, 5, 0, 0 
That is what seems right: 
0, 1, 4, 0, 0 
1, 0, 4, 0, 0 
1, 1, 3, 0, 0 
1, 1, 2, 1, 0 
1, 1, 2, 0, 1 
1, 1, 1, 1, 1 
It takes 6 moves 

The problem weakly reminds a so-called pigeonhole or Dirichlet principle – that n item are put into m containers, with n>m, then at least one container must contain more than one item. Hence it appears in the title.
Approach sounds rather trivial: move along the row of containers from the beginning to the end, if you meet an empty container, fill it, and if you meet a container with several objects (more than one), try to decrease the amount to just one.
More precisely, one must keep a queue of containers with too many objects and another queue of empty places and use them to pass objects. Obviously, since we try to pass objects as soon as there is a possibility, one of the queues will become empty on every step after we make all possible movements. 
Still more detailed: 
Every time we meet an empty place, we check if there is something in the queue of containers with multiple objects, if yes, take one object and fill the empty place. If no, add this empty place to the queue. 
Every time we meet an overcrowded container, check if the queue of empty spaces has something, if yes, try to put objects from the current container as many as possible into these empty containers and remove them from their queue. If no empty ready, push the overcrowded container into the queue for full containers. 
The queue for overcrowded containers keeps pairs of numbers: location and amount of superfluous objects. The queue for empty containers keeps just numbers for locations since they are empty.
If the input is supplied as an array of coordinates of objects A, first reorganize it like an array of amounts per location.
This problem can be in more than one dimensions like in the Codility challenge Selenium 2016, which inspired this article. But since dimensions are independent, the results min every dimension just summarized to get the final answer. 
The full implementation for the problem in Codility includes that the final answer is taken Modulo 10^9+7.
Examples: 
 

Input : 
Coordinates of objects are
X = [1, 2, 2, 3, 4] and array Y = [1, 1, 4, 5, 4]
Output :
5
Input :
X = [1, 1, 1, 1] and array Y = [1, 2, 3, 4]
Output :
6
Input :
X = [1, 1, 2] and array Y = [1, 2, 1]
Output :
4

Note: there is another way to do it, possibly will be covered in another article, inspired by a more hard problem in Codility Future Mobility Challenge. It includes the following steps: 
 

  • Subtract from every location 1, now we strife for all 0 instead of all 1
  • Cut the array into fragments such as in every fragment the movement of objects are all in one direction, starting and trailing zeroes can be dropped for every fragment. Sample: 1, 0, -1, 0, -2, 2 is cut into 1, 0, -1 and -2, 2. The cut points are discovered by zeroes of prefix sum
  • For every fragment calculate the second integral. That is the prefix of prefix from left to right. The most right value is the amount of moves. Sample sequence: 1, 0, -1. The prefix is 1, 1, 0. The second prefix is 1, 2, 2. The answer for this fragment is 2 (two moves from 0 to 2)
  • The result is the sum of results for all fragments

The final implementation of the first way: 

CPP




#include <queue>
#include <vector>
using namespace std;
#define MODULO 1000000007
 
/* Utility function for one dimension
unsigned long long solution(vector<int>& A)
Parameters: vector<int>& A - an array of numbers
of objects per container
Return value: How many moves to make all containers
              have one object */
unsigned long long solution(vector<int>& A)
{
    // the final result cannot be less than
    // zero, so we initiate it as 0
    unsigned long long res = 0;
 
    // just to keep the amount of objects for future usage
    const unsigned int len = (unsigned int)A.size();
 
     // The queue of objects that are ready for move,
     // as explained in the introduction. The queue is
     // of pairs, where the first value is the index
     // in the row of containers, the second is the
     // number of objects there currently.
    queue<pair<unsigned int, unsigned int> > depot;
 
     // The queue of vacant containers that are ready
     // to be filled, as explained in the introduction,
     // just the index on the row, since they are
    // empty - no amount, zero is meant.
    queue<unsigned int> depotOfEmpties;
 
    // how many objects have coordinate i
    vector<unsigned int> places(len, 0);
 
    // initiates the data into a more convenient way,
    // not coordinates of objects but how many objects
    // are per place
    for (unsigned int i = 0; i < len; i++) {
        places.at(A.at(i) - 1)++;
    }
 
    // main loop, movement along containers as
    // explained in the introduction
    for (unsigned int i = 0; i < len; i++) {
 
        // if we meet the empty container at place i
        if (0 == places.at(i)) {
 
            // we check that not objects awaiting
            // to movement, the queue of objects
            // is possible empty
            if (depot.empty()) {
 
                 // if true, no object to move, we
                 // insert the empty container into
                 // a queue of empties 
                depotOfEmpties.push(i);
            }
 
            // there are some object to move, take
            // the first from the queue  
            else {
 
                // and find how distant it is
                unsigned int distance = (i - depot.front().first);
 
                // we move one object and making
                // "distance" moves by it
                res += distance;
 
                // since the result is expected MODULO
                res = res % MODULO;
 
                // now one object left the queue
                // for movement, so we discount it
                depot.front().second--;
 
                 // if some elements in the objects
                 // queue may loose all objects, 
                while (!depot.empty() && depot.front().second < 1) {
                    depot.pop(); // remove all them from the queue
                }
            }
        }
 
        // places.at(i) > 0, so we found the current
        // container not empty
        else {
 
            // if it has only one object, nothing must
            // be done
            if (1 == places.at(i)) {
 
                // so the object remains in its place,
                // go further
                continue;            
            }
 
            // there are more than one there, need
            // to remove some
            else {
 
                // how many to remove? To leave one
                unsigned int pieces = places.at(i) - 1;
 
                // how many empty places are awaiting to fill
                // currently? Are they enough to remove "pieces"?
                unsigned int lenEmptySequence = depotOfEmpties.size();
 
                // Yes, we have places for all objects
                // to remove from te current 
                if (pieces <= lenEmptySequence) {
 
                    // move all objects except one
                    for (unsigned int j = 0; j < pieces; j++) {
 
                        // add to the answer and apply MODULOto
                        // prevent overflow
                        res = (res + i - depotOfEmpties.front()) % MODULO;
 
                        // remove former empty from the queue of empties
                        depotOfEmpties.pop();
                    }
                }
 
                // empty vacancies are not enough or absent at all
                else {
                    for (unsigned int j = 0; j < lenEmptySequence; j++) {
 
                        // fill what we can
                        res = (res + i - depotOfEmpties.front()) % MODULO;
 
                        // and remove filled from the vacancies queue 
                        depotOfEmpties.pop();
                    }
 
                    // since we still have too many objects in
                    // this container, push it into the queue for
                    // overcrowded containers
                    depot.push(pair<unsigned int, unsigned int>(i,
                                        pieces - lenEmptySequence));
                }
            }
        }
    }
 
    // the main loop end
    return res; // return the result
}
 
/* Main function for two dimensions as in
   Codility problem
int solution(vector<int>& A, vector<int>& B)
Parameters:
 vector<int>& A - coordinates x of the objects
 vector<int>& B - coordinates y of the objects
Return value:
 No. of moves to make all verticals and horizontals
 have one object
*/
int solution(vector<int>& A, vector<int>& B)
{
    unsigned long long res = solution(B);
    res += solution(A);
    res = res % MODULO;
    return (int)res;
}
 
// test utility for the driver below
#include <iostream>
void test(vector<int>& A, vector<int>& B, int expected,
         bool printAll = false, bool doNotPrint = true)
{
    int res = solution(A, B);
    if ((expected != res && !doNotPrint) || printAll) {
        for (size_t i = 0; i < A.size(); i++) {
            cout << A.at(i) << " ";
        }
        cout << endl;
        for (size_t i = 0; i < B.size(); i++) {
            cout << B.at(i) << " ";
        }
        cout << endl;
        if (expected != res)
            cout << "Error! Expected: " << expected << "  ";
        else
            cout << "Expected: " << expected << "  ";
    }
    cout << " Result: " << res << endl;
}
 
// Driver (main)
int main()
{
    int A4[] = { 1, 2, 2, 3, 4 };
    int B4[] = { 1, 1, 4, 5, 4 };
    vector<int> VA(A4, A4 + 5);
    vector<int> VB(B4, B4 + 5);
    test(VA, VB, 5);
 
    int A0[] = { 1, 1, 1, 1 };
    int B0[] = { 1, 2, 3, 4 };
    VA = vector<int>(A0, A0 + 4);
    VB = vector<int>(B0, B0 + 4);
    test(VA, VB, 6);
 
    int A2[] = { 1, 1, 2 };
    int B2[] = { 1, 2, 1 };
    VA = vector<int>(A2, A2 + 3);
    VB = vector<int>(B2, B2 + 3);
    test(VA, VB, 4);
 
    // square case
    int A3[] = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
    int B3[] = { 1, 1, 1, 2, 2, 2, 3, 3, 3 };
    VA = vector<int>(A3, A3 + 9);
    VB = vector<int>(B3, B3 + 9);
    test(VA, VB, 54);
    // also
    int A5[] = { 7, 8, 9, 7, 8, 9, 7, 8, 9 };
    int B5[] = { 7, 7, 7, 8, 8, 8, 9, 9, 9 };
    VA = vector<int>(A5, A5 + 9);
    VB = vector<int>(B5, B5 + 9);
    test(VA, VB, 54);
 
    int A6[] = { 1, 1, 2, 3 };
    int B6[] = { 1, 2, 3, 4 };
    VA = vector<int>(A6, A6 + 4);
    VB = vector<int>(B6, B6 + 4);
    test(VA, VB, 3);
    test(VB, VA, 3);
 
    int A7[] = { 1, 1, 3, 5, 5 };
    int B7[] = { 1, 5, 3, 1, 5 };
    VA = vector<int>(A7, A7 + 5);
    VB = vector<int>(B7, B7 + 5);
    test(VA, VB, 4);
    test(VB, VA, 4);
 
    // smaller square case
    int A8[] = { 1, 2, 1, 2 };
    int B8[] = { 1, 1, 2, 2 };
    VA = vector<int>(A8, A8 + 4);
    VB = vector<int>(B8, B8 + 4);
    test(VA, VB, 8);
 
    int A9[] = { 3, 4, 3, 4 };
    int B9[] = { 3, 3, 4, 4 };
    VA = vector<int>(A9, A9 + 4);
    VB = vector<int>(B9, B9 + 4);
    test(VA, VB, 8);
 
    int A10[] = { 1, 2, 3, 4, 1, 2, 3, 4, 1,
                     2, 3, 4, 1, 2, 3, 4 };
    int B10[] = { 1, 1, 1, 1, 2, 2, 2, 2, 3,
                      3, 3, 3, 4, 4, 4, 4 };
    VA = vector<int>(A10, A10 + 16);
    VB = vector<int>(B10, B10 + 16);
    test(VA, VB, 192);
 
    int A11[] = { 13, 14, 15, 16, 13, 14, 15,
         16, 13, 14, 15, 16, 13, 14, 15, 16 };
    int B11[] = { 13, 13, 13, 13, 14, 14, 14,
         14, 15, 15, 15, 15, 16, 16, 16, 16 };
    VA = vector<int>(A11, A11 + 16);
    VB = vector<int>(B11, B11 + 16);
    test(VA, VB, 192);
    return 0;
}


Java




import java.util.*;
import java.util.AbstractMap.SimpleEntry;
 
public class Main {
 
    private static final int MODULO = 1000000007;
 
    /**
     * Utility function for one dimension.
     *
     * @param A - an array of numbers representing the number of objects per container.
     * @return The number of moves to make all containers have one object.
     */
    private static long solution(int[] A) {
        long res = 0;
        int len = A.length;
 
        Queue<SimpleEntry<Integer, Integer>> depot = new LinkedList<>();
        Queue<Integer> depotOfEmpties = new LinkedList<>();
        List<Integer> places = new ArrayList<>(Collections.nCopies(len, 0));
 
        // Initialize places based on the input array
        for (int i = 0; i < len; i++) {
            places.set(A[i] - 1, places.get(A[i] - 1) + 1);
        }
 
        // Main loop to move objects between containers
        for (int i = 0; i < len; i++) {
            if (places.get(i) == 0) {
                if (depot.isEmpty()) {
                    depotOfEmpties.add(i);
                } else {
                    int distance = i - depot.peek().getKey();
                    res += distance;
                    res %= MODULO;
                    depot.peek().setValue(depot.peek().getValue() - 1);
                    while (!depot.isEmpty() && depot.peek().getValue() < 1) {
                        depot.poll();
                    }
                }
            } else {
                if (places.get(i) == 1) {
                    continue;
                } else {
                    int pieces = places.get(i) - 1;
                    int lenEmptySequence = depotOfEmpties.size();
 
                    if (pieces <= lenEmptySequence) {
                        for (int j = 0; j < pieces; j++) {
                            res = (res + i - depotOfEmpties.poll()) % MODULO;
                        }
                    } else {
                        for (int j = 0; j < lenEmptySequence; j++) {
                            res = (res + i - depotOfEmpties.poll()) % MODULO;
                        }
                        depot.add(new SimpleEntry<>(i, pieces - lenEmptySequence));
                    }
                }
            }
        }
        return res;
    }
 
    /**
     * Main function for two dimensions.
     *
     * @param A - coordinates x of the objects.
     * @param B - coordinates y of the objects.
     * @return The number of moves to make all verticals and horizontals have one object.
     */
    private static int solution(int[] A, int[] B) {
        long res = solution(B) + solution(A);
        res %= MODULO;
        return (int) res;
    }
 
    public static void main(String[] args) {
        int[] A4 = {1, 2, 2, 3, 4};
        int[] B4 = {1, 1, 4, 5, 4};
        test(A4, B4, 5);
 
        int[] A0 = {1, 1, 1, 1};
        int[] B0 = {1, 2, 3, 4};
        test(A0, B0, 6);
 
        int[] A2 = {1, 1, 2};
        int[] B2 = {1, 2, 1};
        test(A2, B2, 4);
 
        // square case
        int[] A3 = {1, 2, 3, 1, 2, 3, 1, 2, 3};
        int[] B3 = {1, 1, 1, 2, 2, 2, 3, 3, 3};
        test(A3, B3, 54);
 
        int[] A5 = {7, 8, 9, 7, 8, 9, 7, 8, 9};
        int[] B5 = {7, 7, 7, 8, 8, 8, 9, 9, 9};
        test(A5, B5, 54);
 
        int[] A6 = {1, 1, 2, 3};
        int[] B6 = {1, 2, 3, 4};
        test(A6, B6, 3);
        test(B6, A6, 3);
 
        int[] A7 = {1, 1, 3, 5, 5};
        int[] B7 = {1, 5, 3, 1, 5};
        test(A7, B7, 4);
        test(B7, A7, 4);
 
        // smaller square case
        int[] A8 = {1, 2, 1, 2};
        int[] B8 = {1, 1, 2, 2};
        test(A8, B8, 8);
 
        int[] A9 = {3, 4, 3, 4};
        int[] B9 = {3, 3, 4, 4};
        test(A9, B9, 8);
 
        int[] A10 = {1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4};
        int[] B10 = {1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4};
        test(A10, B10, 192);
 
        int[] A11 = {13, 14, 15, 16, 13, 14, 15, 16, 13, 14, 15, 16, 13, 14, 15, 16};
        int[] B11 = {13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16};
        test(A11, B11, 192);
    }
 
    /**
     * Test utility function to compare expected and actual results.
     *
     * @param A        - Array representing container data in one dimension.
     * @param B        - Array representing container data in the other dimension.
     * @param expected - Expected result.
     */
    private static void test(int[] A, int[] B, int expected) {
        int res = solution(A, B);
        System.out.println("Result: " + res);
    }
}


Python3




# Python equivalent
# MODULO is the same
MODULO = 1000000007
def solution(A):
    # the final result cannot be less than zero, so we initiate it as 0
    res = 0
    # just to keep the amount of objects for future usage
    length = len(A)
    # The queue of objects that are ready for move, as explained in the introduction. The queue is of pairs, where the first value is the index  in the row of containers, the second is the number of objects there currently.
    depot = []
    # The queue of vacant containers that are ready to be filled, as explained in the introduction, just the index on the row, since they are  empty - no amount, zero is meant.
    depot_of_empties = []
    # how many objects have coordinate i
    places = [0] * length
    # initiates the data into a more convenient way, not coordinates of objects but how many objects are per place
    for i in range(length):
        places[A[i] - 1] += 1
    # main loop, movement along containers as explained in the introduction
    for i in range(length):
        # if we meet the empty container at place i
        if places[i] == 0:
            # we check that not objects awaiting  to movement, the queue of objects is possible empty
            if len(depot) == 0:
                # if true, no object to move, we insert the empty container into a queue of empties
                depot_of_empties.append(i)
            # there are some object to move, take the first from the queue
            else:
                # and find how distant it is
                distance = (i - depot[0][0])
                # we move one object and making "distance" moves by it
                res += distance
                # since the result is expected MODULO
                res %= MODULO
                # now one object left the queue for movement, so we discount it
                depot[0][1] -= 1
                # if some elements in the objects queue may loose all objects,
                while len(depot) != 0 and depot[0][1] < 1:
                    depot.pop(0# remove all them from the queue
        # places[i] > 0, so we found the current container not empty
        else:
            # if it has only one object, nothing must be done
            if places[i] == 1:
                # so the object remains in its place, go further
                continue
            # there are more than one there, need to remove some
            else:
                # how many to remove? To leave one
                pieces = places[i] - 1
                # how many empty places are awaiting to fill currently? Are they enough to remove "pieces"?
                len_empty_sequence = len(depot_of_empties)
                # Yes, we have places for all objects to remove from the current
                if pieces <= len_empty_sequence:
                    # move all objects except one
                    for j in range(pieces):
                        # add to the answer and apply MODULO to prevent overflow
                        res = (res + i - depot_of_empties[0]) % MODULO
                        # remove former empty from the queue of empties
                        depot_of_empties.pop(0)
                # empty vacancies are not enough or absent at all
                else:
                    for j in range(len_empty_sequence):
                        # fill what we can
                        res = (res + i - depot_of_empties[0]) % MODULO
                        # and remove filled from the vacancies queue
                        depot_of_empties.pop(0)
                    # since we still have too many objects in this container, push it into the queue for overcrowded containers
                    depot.append([i, pieces - len_empty_sequence])
    # the main loop end
    return res  # return the result
# Main function for two dimensions as in
# Codility problem
def solution_2(A, B):
    res = solution(B)
    res += solution(A)
    res %= MODULO
    return int(res)
# test utility for the driver below
def test(A, B, expected, print_all=False, do_not_print=True):
    res = solution_2(A, B)
    if (expected != res and not do_not_print) or print_all:
        print(A)
        print(B)
        if expected != res:
            print("Error! Expected: ", expected, "  ", end='')
        else:
            print("Expected: ", expected, "  ", end='')
    print(" Result: ", res)
# Driver (main)
if __name__ == "__main__":
    A4 = [1, 2, 2, 3, 4]
    B4 = [1, 1, 4, 5, 4]
    test(A4, B4, 5)
    A0 = [1, 1, 1, 1]
    B0 = [1, 2, 3, 4]
    test(A0, B0, 6)
    A2 = [1, 1, 2]
    B2 = [1, 2, 1]
    test(A2, B2, 4)
    # square case
    A3 = [1, 2, 3, 1, 2, 3, 1, 2, 3]
    B3 = [1, 1, 1, 2, 2, 2, 3, 3, 3]
    test(A3, B3, 54)
    # also
    A5 = [7, 8, 9, 7, 8, 9, 7, 8, 9]
    B5 = [7, 7, 7, 8, 8, 8, 9, 9, 9]
    test(A5, B5, 54)
    A6 = [1, 1, 2, 3]
    B6 = [1, 2, 3, 4]
    test(A6, B6, 3)
    test(B6, A6, 3)
    A7 = [1, 1, 3, 5, 5]
    B7 = [1, 5, 3, 1, 5]
    test(A7, B7, 4)
    test(B7, A7, 4)
    # smaller square case
    A8 = [1, 2, 1, 2]
    B8 = [1, 1, 2, 2]
    test(A8, B8, 8)
 
    A9 = [3, 4, 3, 4]
    B9 = [3, 3, 4, 4]
    test(A9, B9, 8)
 
    A10 = [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
    B10 = [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4]
    test(A10, B10, 192)
 
    A11 = [13, 14, 15, 16, 13, 14, 15, 16, 13, 14, 15, 16, 13, 14, 15, 16]
    B11 = [13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16]
    test(A11, B11, 192)


C#




using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
//C# program for the above approach
 
 
class HelloWorld {
  
    public static int MODULO = 1000000007;
    /* Utility function for one dimension
    unsigned long long solution(vector<int>& A)
    Parameters: vector<int>& A - an array of numbers
    of objects per container
    Return value: How many moves to make all containers
                  have one object */
    public static int solution(int[] A)
    {
        // the final result cannot be less than
        // zero, so we initiate it as 0
        int res = 0;
 
        // just to keep the amount of objects for future usage
        int len = A.Length;
 
         // The queue of objects that are ready for move,
         // as explained in the introduction. The queue is
         // of pairs, where the first value is the index
         // in the row of containers, the second is the
         // number of objects there currently.
        Queue depot = new Queue();
 
         // The queue of vacant containers that are ready
         // to be filled, as explained in the introduction,
         // just the index on the row, since they are
        // empty - no amount, zero is meant.
        Queue depotOfEmpties = new Queue();
 
        // how many objects have coordinate i
        int[] places = new int[len];
        for(int i = 0; i < len; i++) places[i] = 0;
 
        // initiates the data into a more convenient way,
        // not coordinates of objects but how many objects
        // are per place
        for (int i = 0; i < len; i++) {
            places[A[i]-1]++;
        }
 
        // main loop, movement along containers as
        // explained in the introduction
        for (int i = 0; i < len; i++) {
 
            // if we meet the empty container at place i
            if (0 == places[i]) {
 
                // we check that not objects awaiting
                // to movement, the queue of objects
                // is possible empty
                if (depot.Count == 0) {
 
                     // if true, no object to move, we
                     // insert the empty container into
                     // a queue of empties 
                    depotOfEmpties.Enqueue(i);
                }
 
                // there are some object to move, take
                // the first from the queue  
                else {
 
                    // and find how distant it is
                    int distance = (i - ((KeyValuePair<int,int>)depot.Peek()).Key);
 
                    // we move one object and making
                    // "distance" moves by it
                    res += distance;
 
                    // since the result is expected MODULO
                    res = res % MODULO;
 
                    // now one object left the queue
                    // for movement, so we discount it
                    int curr_key = ((KeyValuePair<int,int>)depot.Peek()).Key;
                    int curr_val = ((KeyValuePair<int,int>)depot.Peek()).Value-1;
                    depot.Dequeue();
                    depot.Enqueue(new KeyValuePair<int,int>(curr_key, curr_val));
 
                     // if some elements in the objects
                     // queue may loose all objects, 
                    while (depot.Count > 0 && ((KeyValuePair<int,int>)depot.Peek()).Value < 1) {
                        depot.Dequeue(); // remove all them from the queue
                    }
                }
            }
 
            // places.at(i) > 0, so we found the current
            // container not empty
            else {
 
                // if it has only one object, nothing must
                // be done
                if (1 == places[i]) {
 
                    // so the object remains in its place,
                    // go further
                    continue;            
                }
 
                // there are more than one there, need
                // to remove some
                else {
 
                    // how many to remove? To leave one
                    int pieces = places[i] - 1;
 
                    // how many empty places are awaiting to fill
                    // currently? Are they enough to remove "pieces"?
                    int lenEmptySequence = depotOfEmpties.Count;
 
                    // Yes, we have places for all objects
                    // to remove from te current 
                    if (pieces <= lenEmptySequence) {
 
                        // move all objects except one
                        for (int j = 0; j < pieces; j++) {
 
                            // add to the answer and apply MODULOto
                            // prevent overflow
                            res = (res + i - (int)depotOfEmpties.Peek())% MODULO;
 
                            // remove former empty from the queue of empties
                            depotOfEmpties.Dequeue();
                        }
                    }
 
                    // empty vacancies are not enough or absent at all
                    else {
                        for (int j = 0; j < lenEmptySequence; j++) {
 
                            // fill what we can
                            res = (res + i - (int)depotOfEmpties.Peek()) % MODULO;
 
                            // and remove filled from the vacancies queue 
                            depotOfEmpties.Dequeue();
                        }
 
                        // since we still have too many objects in
                        // this container, push it into the queue for
                        // overcrowded containers
                        depot.Enqueue(new KeyValuePair<int,int>(i, pieces-lenEmptySequence));
                    }
                }
            }
        }
 
        // the main loop end
        return res; // return the result
    }
 
    /* Main function for two dimensions as in
       Codility problem
    int solution(vector<int>& A, vector<int>& B)
    Parameters:
     vector<int>& A - coordinates x of the objects
     vector<int>& B - coordinates y of the objects
    Return value:
     No. of moves to make all verticals and horizontals
     have one object
    */
    public static int solution(int[] A, int[] B)
    {
        int res = solution(B);
        res += solution(A);
        res = res % MODULO;
        return res;
    }
 
    // test utility for the driver below
    public static void test(int[] A, int[] B, int expected, bool printAll = false, bool doNotPrint = true)
    {
        int res = solution(A, B);
        if ((expected != res && !doNotPrint) || printAll) {
            for (int i = 0; i < A.Length; i++) {
                Console.Write(A[i] + " ");
            }
            Console.WriteLine();
            for (int i = 0; i < B.Length; i++) {
                Console.Write(B[i] + " ");
            }
            Console.WriteLine();
            if (expected != res)
                Console.Write("Error! Expected: " + expected + " ");
            else
                Console.Write("Expected: " + expected + " ");
        }
        Console.WriteLine(" Result: " + res);
    }
 
    static void Main() {
 
        int[] A4 = { 1, 2, 2, 3, 4 };
        int[] B4 = { 1, 1, 4, 5, 4 };
        test(A4, B4, 5);
 
        int[] A0 = { 1, 1, 1, 1 };
        int[] B0 = { 1, 2, 3, 4 };
        test(A0, B0, 6);
 
        int[] A2 = { 1, 1, 2 };
        int[] B2 = { 1, 2, 1 };
        test(A2, B2, 4);
 
        // square case
        int[] A3 = { 1, 2, 3, 1, 2, 3, 1, 2, 3 };
        int[] B3 = { 1, 1, 1, 2, 2, 2, 3, 3, 3 };
        test(A3, B3, 54);
        // also
        int[] A5 = { 7, 8, 9, 7, 8, 9, 7, 8, 9 };
        int[] B5 = { 7, 7, 7, 8, 8, 8, 9, 9, 9 };
        test(A5, B5, 54);
 
        int[] A6 = { 1, 1, 2, 3 };
        int[] B6 = { 1, 2, 3, 4 };
        test(A6, B6, 3);
        test(A6, B6, 3);
 
        int[] A7 = { 1, 1, 3, 5, 5 };
        int[] B7 = { 1, 5, 3, 1, 5 };
        test(A7, B7, 4);
        test(A7, B7, 4);
 
        // smaller square case
        int[] A8 = { 1, 2, 1, 2 };
        int[] B8 = { 1, 1, 2, 2 };
        test(A8, B8, 8);
 
        int[] A9 = { 3, 4, 3, 4 };
        int[] B9 = { 3, 3, 4, 4 };
        test(A9, B9, 8);
 
        int[] A10 = { 1, 2, 3, 4, 1, 2, 3, 4, 1,
                         2, 3, 4, 1, 2, 3, 4 };
        int[] B10 = { 1, 1, 1, 1, 2, 2, 2, 2, 3,
                          3, 3, 3, 4, 4, 4, 4 };
        test(A10, B10, 192);
 
        int[] A11 = { 13, 14, 15, 16, 13, 14, 15,
             16, 13, 14, 15, 16, 13, 14, 15, 16 };
        int[] B11 = { 13, 13, 13, 13, 14, 14, 14,
             14, 15, 15, 15, 15, 16, 16, 16, 16 };
        test(A11, B11, 192);
    }
}
 
// The code is contributed by Nidhi goel.


Javascript




// Javascript code implementation
 
let MODULO = 1000000007
 
/* Utility function for one dimension
unsigned long long solution(vector<int>& A)
Parameters: vector<int>& A - an array of numbers
of objects per container
Return value: How many moves to make all containers
              have one object */
function sol(A)
{
 
    // the final result cannot be less than
    // zero, so we initiate it as 0
    let res = 0;
 
    // just to keep the amount of objects for future usage
    let len = A.length;
 
     // The queue of objects that are ready for move,
     // as explained in the introduction. The queue is
     // of pairs, where the first value is the index
     // in the row of containers, the second is the
     // number of objects there currently.
    let depot = [];
 
     // The queue of vacant containers that are ready
     // to be filled, as explained in the introduction,
     // just the index on the row, since they are
    // empty - no amount, zero is meant.
    let depotOfEmpties = [];
 
    // how many objects have coordinate i
    let places = new Array(len).fill(0);
 
    // initiates the data into a more convenient way,
    // not coordinates of objects but how many objects
    // are per place
    for (let i = 0; i < len; i++) {
        places[A[i] - 1]++;
    }
 
    // main loop, movement along containers as
    // explained in the introduction
    for (let i = 0; i < len; i++) {
 
        // if we meet the empty container at place i
        if (0 == places[i]) {
 
            // we check that not objects awaiting
            // to movement, the queue of objects
            // is possible empty
            if (depot.length == 0) {
 
                 // if true, no object to move, we
                 // insert the empty container into
                 // a queue of empties 
                depotOfEmpties.push(i);
            }
 
            // there are some object to move, take
            // the first from the queue  
            else {
 
                // and find how distant it is
                let distance = (i - depot[0][0]);
 
                // we move one object and making
                // "distance" moves by it
                res += distance;
 
                // since the result is expected MODULO
                res = res % MODULO;
 
                // now one object left the queue
                // for movement, so we discount it
                depot[0][1]--;
 
                 // if some elements in the objects
                 // queue may loose all objects, 
                while (depot.length > 0 && depot[0][1] < 1) {
                    depot.shift(); // remove all them from the queue
                }
            }
        }
 
        // places.at(i) > 0, so we found the current
        // container not empty
        else {
 
            // if it has only one object, nothing must
            // be done
            if (1 == places[i]) {
 
                // so the object remains in its place,
                // go further
                continue;            
            }
 
            // there are more than one there, need
            // to remove some
            else {
 
                // how many to remove? To leave one
                let pieces = places[i] - 1;
 
                // how many empty places are awaiting to fill
                // currently? Are they enough to remove "pieces"?
                let lenEmptySequence = depotOfEmpties.length;
 
                // Yes, we have places for all objects
                // to remove from te current 
                if (pieces <= lenEmptySequence) {
 
                    // move all objects except one
                    for (let j = 0; j < pieces; j++) {
 
                        // add to the answer and apply MODULOto
                        // prevent overflow
                        res = (res + i - depotOfEmpties[0]) % MODULO;
 
                        // remove former empty from the queue of empties
                        depotOfEmpties.shift();
                    }
                }
 
                // empty vacancies are not enough or absent at all
                else {
                    for (let j = 0; j < lenEmptySequence; j++) {
 
                        // fill what we can
                        res = (res + i - depotOfEmpties[0]) % MODULO;
 
                        // and remove filled from the vacancies queue 
                        depotOfEmpties.shift();
                    }
 
                    // since we still have too many objects in
                    // this container, push it into the queue for
                    // overcrowded containers
                    depot.push([i, pieces - lenEmptySequence]);
                }
            }
        }
    }
 
    // the main loop end
    return res; // return the result
}
 
/* Main function for two dimensions as in
   Codility problem
int solution(vector<int>& A, vector<int>& B)
Parameters:
 vector<int>& A - coordinates x of the objects
 vector<int>& B - coordinates y of the objects
Return value:
 No. of moves to make all verticals and horizontals
 have one object
*/
function solution(A, B)
{
    let res = sol(B);
    res += sol(A);
    res = res % MODULO;
    return parseInt(res);
}
 
// test utility for the driver below
function test(A, B, expected, printAll, doNotPrint)
{
    printAll = false;
    doNotPrint = true;
    let res = solution(A, B);
    if ((expected != res && !doNotPrint) || printAll) {
        for (let i = 0; i < A.length; i++) {
            console.log(A[i]);
        }
        for (let i = 0; i < B.length; i++) {
            console.log(B[i]);
        }
        if (expected != res)
            console.log("Error! Expected: " + expected + "  ");
        else
            console.log("Expected: " + expected + "  ");
    }
    console.log(" Result: " + res);
}
 
// Driver (main)
let A4 = [ 1, 2, 2, 3, 4 ];
let B4 = [ 1, 1, 4, 5, 4 ];
VA = A4;
VB = B4;
test(VA, VB, 5);
 
let A0 = [ 1, 1, 1, 1 ];
let B0 = [ 1, 2, 3, 4 ];
VA = A0;
VB = B0;
test(VA, VB, 6);
 
let A2 = [ 1, 1, 2 ];
let B2 = [ 1, 2, 1 ];
VA = A2;
VB = B2;
test(VA, VB, 4);
 
// square case
let A3 = [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ];
let B3 = [ 1, 1, 1, 2, 2, 2, 3, 3, 3 ];
VA = A3;
VB = B3;
test(VA, VB, 54);
// also
let A5 = [7, 8, 9, 7, 8, 9, 7, 8, 9 ];
let B5 = [7, 7, 7, 8, 8, 8, 9, 9, 9 ];
VA = A5;
VB = B5;
test(VA, VB, 54);
 
let A6 = [ 1, 1, 2, 3 ];
let B6 = [ 1, 2, 3, 4 ];
VA = A6;
VB = B6;
test(VA, VB, 3);
test(VB, VA, 3);
 
let A7 = [ 1, 1, 3, 5, 5 ];
let B7 = [ 1, 5, 3, 1, 5 ];
VA = A7;
VB = B7;
test(VA, VB, 4);
test(VB, VA, 4);
 
// smaller square case
let A8 = [ 1, 2, 1, 2 ];
let B8 = [ 1, 1, 2, 2 ];
VA = A8;
VB = B8;
test(VA, VB, 8);
 
let A9 = [ 3, 4, 3, 4 ];
let B9 = [ 3, 3, 4, 4 ];
VA = A9;
VB = B9;
test(VA, VB, 8);
 
let A10 = [ 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4 ];
let B10 = [ 1, 1, 1, 1, 2, 2, 2, 2, 3,  3, 3, 3, 4, 4, 4, 4 ];
VA = A10;
VB = B10;
test(VA, VB, 192);
 
let A11 = [13, 14, 15, 16, 13, 14, 15, 16, 13, 14, 15, 16, 13, 14, 15, 16 ];
let B11 = [13, 13, 13, 13, 14, 14, 14,  14, 15, 15, 15, 15, 16, 16, 16, 16 ];
VA = A11;
VB = B11;
test(A11, B11, 192);
 
// The code is contributed by Nidhi goel.


Output

Result: 5
 Result: 6
 Result: 4
 Result: 54
 Result: 54
 Result: 3
 Result: 3
 Result: 4
 Result: 4
 Result: 8
 Result: 8
 Result: 192
 Result: 192


Last Updated : 11 Oct, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads