Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/130.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 以最少的移动交换箱子_C++_C_Algorithm - Fatal编程技术网

C++ 以最少的移动交换箱子

C++ 以最少的移动交换箱子,c++,c,algorithm,C++,C,Algorithm,这是一个节目竞赛(已经结束)中的问题。我正在努力解决这个问题,但找不到一个健康的方法来解决 问题如下: IIIT Allahabad将于10月1日至5日举行一年一度的技术文化节MM12。厨师已经同意为这个节日供应糖果。厨师准备了N盒糖果,编号为1到N(每个数字正好出现一次)。厨师对盒子的摆放非常讲究。他想把盒子按特定的顺序排列,但不幸的是厨师很忙。他让你为他重新安排箱子。给定框的当前顺序,必须按指定顺序重新排列框。但是有一个限制。您只能交换两个相邻的箱子以获得所需的订单。输出,所需的此类相邻交换

这是一个节目竞赛(已经结束)中的问题。我正在努力解决这个问题,但找不到一个健康的方法来解决

问题如下:

IIIT Allahabad将于10月1日至5日举行一年一度的技术文化节MM12。厨师已经同意为这个节日供应糖果。厨师准备了N盒糖果,编号为1到N(每个数字正好出现一次)。厨师对盒子的摆放非常讲究。他想把盒子按特定的顺序排列,但不幸的是厨师很忙。他让你为他重新安排箱子。给定框的当前顺序,必须按指定顺序重新排列框。但是有一个限制。您只能交换两个相邻的箱子以获得所需的订单。输出,所需的此类相邻交换的最小数量

输入

输入的第一行包含一个整数T,即测试用例数。每个测试用例包含3行,第一行包含一个整数N,框数。接下来的两行各包含N个数字,第一行是框的给定顺序,第二行是所需顺序

输出

对于每个测试用例,输出单个整数“K”,即所需的最小相邻交换数。 限制条件:

1<=T<=10
1<=N<=10^5
输出:

2
3
6
3

我对这个问题几乎一无所知。有人能解释一下这个问题背后的逻辑吗

我无法从数学上证明这一点,但在4/4的测试用例中,您通过从最左边开始(也可以从最右边开始)并向右移动,将框置于正确的位置,从而获得最小交换。即

3 4 5 2 1 //First get the 4 in the right place
4 3 5 2 1 //Done.  Now get the 1 in the right place
4 3 5 1 2
4 3 1 5 2
4 1 3 5 2 //Done.  Now the 5
4 1 5 3 2 //Done.  Now the 2
4 1 5 2 3 //All done.

所以这个算法看起来会给出任何给定输入的最小值。最坏的情况通常看起来像是反转,需要N*(N-1)/2次交换(见例2)。

假设所需的顺序是数字的排序顺序,问题归结为在数组中查找反转的数量

如果
i
array[i]>array[j]
,则称
对(i,j)
为反转。这是因为相邻元素之间的每次(最佳)交换都会将反转次数精确地减少
1
。您可以通过一种非常类似于合并排序的分治算法在
O(n log n)
中找到反转数。这是一个很好的解释

编辑证明反转数等于最佳互换数:


i
设为
数组中的任意位置。交换
array[i]
array[i+1]
最多可减少1次反转。因此,所需的互换数量至少等于互换数量。另一方面,如果
array
没有排序,我们总是可以找到一对
(i,i+1)
,这样
array[i]>array[i+1]
(即
(i,j)
是一个反转),并通过将
array[i]
array[i+1]
交换,将反转数减少1。因此,反转的数量等于交换的最小数量。

将源列表减少为(1,2,…,N)的排列。(通过将目标的倒数应用于源)

然后计算倒数的数目

向量源=。。。; 向量目标=。。。; 向量inv(N) 对于(int i=0;i
然后使用标准算法计算perm中的反转数。

该问题是一个相当“经典”的竞争编程问题,即在数组中计算反转数。反转定义为一对(i,j),其中ia[j]

最通用的版本是,给定一个任意数的数组,并要求您计算反转数,它有一个

更严格的版本^,其中数组中的最大值有一个合理的上限(注意,这不是数组的长度),可以在O(n log m)中求解,其中m是数组中的最大值。这里的要点是,与合并排序方法相比,您必须编写的代码量要少得多

问题中的问题是计算交换数以将数组按一定顺序排序,这可以重构为计算交换数以按升序对数组排序,它归结为计算反转数。为什么会出现反转数?因为每次交换2个相邻元素最多只能解析一个反转

您需要创建一个数组来描述框相对于最终设置的当前位置。然后,算法可以启动:

  • 构造一个长度为m(对于问题中的问题,m=n)的二叉索引树

    我们将使用Fenwick树来帮助我们计算数组中大于当前元素的前面元素的数量。我们将保持到目前为止遇到的数字的频率,并使用Fenwick树范围和查询来获得小于当前元素的元素数(并导出大于当前元素的元素数)

  • 通过数组的n个元素循环:

    • 使用范围和查询计算已记录的小于当前数字的数字数量
    • 使用上面的信息找出比当前数字大的数字数量。将其添加到反转计数中。注意不要包括正在考虑的要素。(*)
    • 将+1调整为元素值处的Fenwick树
  • 在(*)步骤中累积的反转计数

  • ^问题清楚地表明元素是唯一的,所以上面的算法可以工作。我只是不确定唯一性是必要条件,还是可以修改算法以适应存在唯一性的情况
    3 4 5 2 1 //First get the 4 in the right place
    4 3 5 2 1 //Done.  Now get the 1 in the right place
    4 3 5 1 2
    4 3 1 5 2
    4 1 3 5 2 //Done.  Now the 5
    4 1 5 3 2 //Done.  Now the 2
    4 1 5 2 3 //All done.
    
    vector<int> source = ...;
    vector<int> target = ...;
    
    vector<int> inv(N)
    
    for (int i = 0; i < N; i++)
       inv[target[i]] = i;
    
    vector<int> perm(N);
    
    for (int i = 0; i < N; i++)
        perm[i] = source[inv[i]];
    
    3 4 5 2 1  
    4 1 5 2 3  
    
    (3 4 5 2 1) to (5 1 3 4 2) 
    
    5 1 3 4 2
    
    5 1 3 4 2