📌  相关文章
📜  如何通过相邻移动将N个对象均匀放置到N个位置

📅  最后修改于: 2021-04-29 19:00:12             🧑  作者: Mango

假设您有N个对象在N个容器中分布不均。您可以将一个对象从一个容器移动到相邻的容器,这被视为一次移动。有什么策略可以最大程度地减少移动次数,以保持时间复杂度O(N)?
例子:

这个问题微弱地提醒了所谓的“鸽洞”或狄里克雷原理-将n个物品放入m个容器(n> m)中,那么至少一个容器必须容纳一个以上的物品。因此,它出现在标题中。

方法听起来很琐碎:从头到尾沿着一排容器移动,如果遇到一个空容器,则将其填充;如果遇到一个包含多个对象(多个)的容器,请尝试将其减少到仅一。

更准确地说,必须将一列包含太多对象的容器和另一列空白的容器并使用它们传递对象。显然,由于我们尽可能地尝试传递对象,因此在进行所有可能的移动后,每个步骤中的一个队列将变为空。
更详细:
每次遇到一个空的地方时,我们都会检查是否有多个对象的容器队列中有东西,如果是,请拿一个对象并填充空的地方。如果否,则将此空白位置添加到队列中。
每当我们遇到一个人满为患的容器时,请检查空白队列中是否有东西,如果是,请尝试将当前容器中的对象尽可能多地放入这些空白容器中,然后从队列中删除它们。如果没有可用的空容器,则将拥挤的容器推入队列以容纳满的容器。

拥挤的容器队列中有成对的数字:多余物体的位置和数量。空容器的队列仅保留位置编号,因为它们是空的。

如果输入是作为对象A的坐标数组提供的,则首先将其像每个位置的数量数组一样进行重组。

这个问题可能来自多个方面,例如Codility挑战赛Selenium 2016,这启发了本文。但是由于维是独立的,所以最小化每个维的结果仅需汇总即可得出最终答案。
对于Codility中的问题的完整实现包括最终答案取为Modulo 10 ^ 9 + 7。

例子:

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

注意:还有另一种方法可以实现,这可能会在另一篇文章中介绍,这是受Codility Future Mobility Challenge中一个更加棘手的问题的启发。它包括以下步骤:

  • 从每个位置1减去,现在我们争所有0而不是全部1
  • 将数组切成碎片,例如,在每个碎片中,对象的运动都朝一个方向移动,可以删除起始零和结尾零以传送碎片。样本:1、0,-1、0,-2、2被切成1、0,-1和-2、2。切点是通过前缀和的零发现的
  • 对于每个片段,计算第二个积分。那是前缀从左到右的前缀。最正确的值是移动量。采样顺序:1、0,-1。前缀为1、1、0。第二个前缀为1、2、2。此片段的答案为2(从0到2的两次移动)
  • 结果是所有片段的结果之和

最终实现的第一种方式:

#include 
#include 
using namespace std;
#define MODULO 1000000007
  
/* Utility function for one dimension
unsigned long long solution(vector& A)
Parameters: vector& 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& 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 > 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 depotOfEmpties; 
  
    // how many objects have coordinate i 
    vector 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 aply 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(i,
                                        pieces - lenEmptySequence)); 
                }
            }
        }
    } 
  
    // the main loop end
    return res; // return the result
}
  
/* Main function for two dimensions as in 
   Codility problem
int solution(vector& A, vector& B)
Parameters:
 vector& A - coordinates x of the objects
 vector& B - coordinates y of the objects
Return value:
 No. of moves to make all verticals and horizontals
 have one object
*/
int solution(vector& A, vector& B)
{
    unsigned long long res = solution(B);
    res += solution(A);
    res = res % MODULO;
    return (int)res;
}
  
// test utility for the driver below
#include 
void test(vector& A, vector& 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 VA(A4, A4 + 5);
    vector VB(B4, B4 + 5);
    test(VA, VB, 5);
  
    int A0[] = { 1, 1, 1, 1 };
    int B0[] = { 1, 2, 3, 4 };
    VA = vector(A0, A0 + 4);
    VB = vector(B0, B0 + 4);
    test(VA, VB, 6);
  
    int A2[] = { 1, 1, 2 };
    int B2[] = { 1, 2, 1 };
    VA = vector(A2, A2 + 3);
    VB = vector(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(A3, A3 + 9);
    VB = vector(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(A5, A5 + 9);
    VB = vector(B5, B5 + 9);
    test(VA, VB, 54);
  
    int A6[] = { 1, 1, 2, 3 };
    int B6[] = { 1, 2, 3, 4 };
    VA = vector(A6, A6 + 4);
    VB = vector(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(A7, A7 + 5);
    VB = vector(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(A8, A8 + 4);
    VB = vector(B8, B8 + 4);
    test(VA, VB, 8);
  
    int A9[] = { 3, 4, 3, 4 };
    int B9[] = { 3, 3, 4, 4 };
    VA = vector(A9, A9 + 4);
    VB = vector(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(A10, A10 + 16);
    VB = vector(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(A11, A11 + 16);
    VB = vector(B11, B11 + 16);
    test(VA, VB, 192);
    return 0;
}
输出:
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