避免地 - 最小步数放置n个棋子,以便只有每一行和每列?

时间:2015-11-07 20:35:11

标签: algorithm recursion dynamic-programming acm

Avoidland是一个在n×n板上玩n个棋子的谜题。棋子最初被放置在棋盘的正方形上,每平方最多一个棋子。目标是移动棋子以使它们彼此“避开” - 不能有一行或一列具有多个棋子。在一次移动中,一个棋子可以移动到一个相邻的未占用的方块,也就是一个与棋子当前位置共享一侧的方格,并且它上面没有棋子。鉴于典当的初始位置,解决这个难题所需的最小移动次数是多少?

输入 第一行包含整数n,然后是n行。第i行包含第i个pawn的初始行和列坐标,由空格分隔。每个坐标是1到n之间的整数。您可以假设n最多为1000000。

输出 该行包含解决难题所需的最小移动次数。

Sample Input 1   
3
1 3
2 3
3 1

Sample Output 1
1


Sample Input 2  
4
1 4
4 1
1 1
4 4

Sample Output 2
4

我的方法: 该解决方案将要求每个行和列完全具有1个pawn。 对于初始配置,请创建最右侧的列,其中包含每行中的典当数之和。 制作一个底行,其中包含每列中的棋子数之和。 现在我们需要找到使每个数组成为所有1的最小步骤数并添加它们但我很困惑如何做到这一点。

2 个答案:

答案 0 :(得分:2)

我认为你的方法很棒。一旦计算了每行/每列中的典当直方图,就可以使用贪婪算法来计算移动数。

假设我们有一个0,0,3,1直方图,我们需要将其改为1,1,1,1。

很明显,我们也可以将最左边的棋子移动到第一个位置,将最接近左边的第二个棋子移动到第二个位置,等等。

因此,只需遍历直方图并将找到的第i个棋子和位置i(我们想放置它的位置)之间的距离加起来。

e.g。在Python中:

A = [0,0,3,1]

t = 0
i = 0
for pos,count in enumerate(A):
    for k in range(count):
        t += abs(pos-i)
        i += 1
print t

打印答案为3,对应于将一个棋子移动两次,一个棋子左一次。

复杂度为O(n),因为内部循环对每个pawn只执行一次。

答案 1 :(得分:0)

直方图的猜测很酷。这是起点。让我们注意到,例如改变水平直方图对垂直直方图没有影响,反之亦然。因此,您应该在垂直和水平直方图上应用两次算法,但算法是相同的。所以我只会考虑横向的。

现在假设我们在{U(0), U(1), ..., U(n-2), O(n-1)}O(k) U(k)代表k位置上的占用和j未占用的广场。现在很明显,当所有方格都未被占用时,将棋子从i位置移动到j-i位置只是他们位置{..U(i),..O(k1),..,O(k2),..,O(kl),..,O(j),...} 的差异。现在我们将展示一般情况下我们有一些占用的正方形也是如此。假设我们有以下直方图:

O(j)

即。我们希望将U(i)移至l,但我们也会占用O(k1)个方块。让我们先将U(i)移至k1-i:这是在O(k2)步骤中完成的。现在我们将U(k1)移至k2-k1O(j)步骤.....最后将U(kl)移至j-kl(k1-i)+(k2-k1)+(k3-k2)+....+(kl-k(l-1))+(j-kl) => j-i 步骤。我们有什么:

C#

所以我们证明了将一个棋子从一个位置移动到另一个位置只是位置上的差异。

现在0 0 0 3 2 中的实施。我们将通过直方图进行2次并行遍历。要理解它,可以考虑一下例子:

i

j将搜索空位,count > 1将搜索int i = 0, j = 0, c = 0; while(i < n && j < n) { while(h[i] <> 0) i++; while(h[j] < 2) j++; c += Math.Abs(j - i++); h[j] = h[j] - 1; } Firt step: i == 0, j == 3, c == 3, h[3] == 2 Second: i == 1, j == 3, c == 5, h[3] == 1 Third: i == 2, j == 4, c == 7, h[4] == 1 所在的位置。

O(2n)

此算法的复杂性为O(4n) = O(n)。我们有2个直方图,因此整体复杂度为a++ + b-- - c++