以最小的移动交换盒子

时间:2012-09-30 04:36:23

标签: c++ c algorithm

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

问题如下:

IIIT Allahabad将于10月1日至5日庆祝其年度Techno-Cultural Fiesta Effervescence MM12。厨师同意为这个节日提供糖果。厨师准备了N盒糖果,编号为1到N(每个数字恰好出现一次)。厨师非常关注盒子的安排。他希望盒子按特定顺序排列,但不幸的是厨师很忙。他已经要求你为他重新安排盒子。根据框的当前顺序,您必须按指定的顺序重新排列框。但是有一个限制。您只能交换两个相邻的盒子以达到所需的顺序。输出,所需的相邻交换的最小数量。

输入

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

输出

对于每个测试用例,输出一个整数'K',即所需的最小相邻交换数。 约束:

1<=T<=10
1<=N<=10^5

实施例

输入:

4

3
1 2 3
3 1 2

3
1 2 3
3 2 1

5
3 4 5 2 1  
4 1 5 2 3  

4
1 2 3 4
2 3 4 1

输出:

2
3
6
3

我对这个问题几乎一无所知。有人可以解释问题背后的逻辑!!

5 个答案:

答案 0 :(得分:6)

这个问题是一个非常“经典”的竞争性编程问题,它正在计算数组中的反转。反转被定义为一对(i,j),其中i <1。 j和A [i]> A [j]的。

最常见的版本,其中包含任意数字的数组,并且要求您计算反转次数,具有O(n log n) solution by modifying merge sort algorithm

更受限制的版本^,其中数组中最大值上有合理的上限(注意这不是数组的长度),可以在O(n)中求解log m),其中m是数组中的最大值。这里的要点是你必须编写的代码量远远少于合并排序方法。

问题中的问题是计算交换次数以将数组排序到某个顺序,这可以重建为计算交换次数以按升序对数组进行排序,并将其归结为计算数量倒置。为什么反转次数?因为每次交换2个相邻元素时最多只能解决一次反转。

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

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

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

  2. 循环遍历数组的n个元素:

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

  4. ^问题清楚地表明元素是唯一的,因此上面的算法将起作用。我只是不确定唯一性是否是必要条件,或者可以修改算法以适应存在重复元素的情况。

答案 1 :(得分:4)

将源列表减少为(1,2,...,N)的排列。 (通过将目标的反转应用于源)

然后计算反转次数。

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]];

然后使用标准算法计算烫发的反转。

答案 2 :(得分:2)

假设所需的顺序是数字的排序顺序,问题就减少到找到数组中的反转次数。

如果pair (i,j)i < jarray[i] > array[j]被认为是倒置。这是因为相邻元素之间的每次(最佳)交换减少了正好1的反转次数。您可以通过与合并排序非常相似的分而治之算法找到O(n log n)中的反转次数。这是一个很好的解释with C code

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

i成为array中的任何位置。交换array[i]array[i+1]会将反转次数减少最多1.因此,所需的互换次数至少等于反转次数。另一方面,如果未对array进行排序,我们总能找到一对(i, i+1),以便array[i] > array[i+1](即(i,j)是反转),并减少反转次数1,通过array[i]array[i+1]交换。因此,反转次数等于最小交换次数。

答案 3 :(得分:0)

该问题可被视为反转计数问题如下:

由于数字的优先级是按照我们应该排序的顺序给出的。

考虑优先级并将其替换为数字

e.g:

3 4 5 2 1  
4 1 5 2 3  

在上面的测试案例中,我们可以观察到4被分配了优先级1,1被分配了优先级2,5被分配了优先级3,依此类推。  那么为什么不用这些优先级替换原始列表中的数字

即。转换原始列表

(3 4 5 2 1) to (5 1 3 4 2) 

(只是用上面讨论的各自的优先级替换数字)

现在我们的列表已转换为

5 1 3 4 2

我们应该按升序排序。

现在我们只允许相邻的互换,即与冒泡排序有些相关。

冒泡排序所需的掉期数量,等于每个元素右侧元素数量的总和,小于当前元素。

例如:在列表中

5 1 3 4 2

5右侧有4个小于5的元素。

1在右侧有0个小于1的元素。

3右侧有1个小于3的元素。

4右侧有1个小于1的元素。

2在右侧有0个小于2的元素。

现在最后的答案是(4 + 0 + 1 + 1 + 0)= 6.

现在可以使用此处讨论的反转计数来计算上述过程   http://www.geeksforgeeks.org/archives/3968

注意:我获得的答案非常有用,只是详细描述了整个事情。 谢谢

答案 4 :(得分:-1)

我不能用数学证明这一点,但是在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)。