堆叠交换


23

问题

说你有N个命名小号1至S Ñ,其中每个小号ķ(k = 1至N)中包含数k的N份。

例如,当N = 3时,堆栈如下所示:

1  2  3  <- top of stack
1  2  3
1  2  3  <- bottom of stack
=======
1  2  3  <- stack index

这里有3个索引为1、2和3的堆栈,每个堆栈包含N个自己索引的实例。

目的是重新排列N个堆栈,以使它们中的每个堆栈都按从上到下的顺序完全包含数字1到N。

例如,对于N = 3,目标是将堆栈重新排列为:

1  1  1
2  2  2
3  3  3
=======
1  2  3

您可以对堆栈执行的唯一操作是从其中一个堆栈中取出最高编号(弹出),然后立即将其放在另一个堆栈顶部(按下)。这要遵守以下规定:

  • 如果一个数字小于或等于该堆栈上的最高编号,则只能将其压入堆栈。

    • 例如1可以推到堆栈上用123在顶部,但是一个2只能与一个推到堆栈23在顶部(或更高)。

    • 这样的结果是堆栈总是从上到下单调增加

  • 可以从中弹出任何非空堆栈,并且在满足前一个项目符号的情况下,可以将任何堆栈压入其中。

  • 可以将任何数字压入空堆栈。

  • 堆栈没有最大高度限制。

  • 不能创建或销毁堆栈,始终有N个堆栈。

挑战在于决定完成哪些弹出操作以完成堆栈交换,不一定要用最少的动作,而是要确保安全。

(练习一副纸牌是感觉问题的好方法。)

挑战

编写一个程序或函数,该程序或函数采用正整数N(保证为3或更大)。打印或返回一个字符串,该字符串表示从初始状态重新排列堆栈所需的所有弹出按钮操作:

1  2  3  4  5
1  2  3  4  5
1  2  3  4  5
1  2  3  4  5
1  2  3  4  5
=============
1  2  3  4  5

(N = 5例)

到最终状态:

1  1  1  1  1
2  2  2  2  2
3  3  3  3  3
4  4  4  4  4
5  5  5  5  5
=============
1  2  3  4  5

输出中的每一行都必须包含两个数字,并用空格分隔。第一个数字是要从中弹出堆栈的索引,第二个数字是要推入的堆栈索引。按顺序执行所有行的操作应正确​​排列堆栈,而不会违反任何规则。

例如,这是N = 3情况的潜在有效输出:

1 2  [move the top number on stack 1 to the top of stack 2]
1 2  [repeat]
1 2  [repeat]
3 1  [move the top number on stack 3 to the top of stack 1]
2 3  [etc.]
2 3
2 3
2 1
2 1
2 1
3 1
3 1
3 1
3 2
1 2
1 2
1 2
1 3
2 3
2 3
2 3
1 2
3 2
3 1

笔记

  • 您的输出也并不需要是最佳的,唯一正确的。即,您不需要最小化弹出和推送的次数。

    • 因此,如果说,如果反复采取一些措施并立即撤消措施,那将是可以的。
    • 例如2 2,也允许一招弹出并推送到同一堆栈(尽管毫无意义)。
  • 您的输出确实需要确定性和有限性。

  • 请记住,堆栈具有基于1的索引。不允许从0开始的索引。

  • 大于9的N当然应该和个位数N一样工作。

  • 如果需要,您可以使用任何两个不同的非数字可打印ASCII字符代替空格和换行符。输出中的尾随换行符(或换行符替换)很好。

计分

以字节为单位的最短代码获胜。决胜局是最高投票的答案。

如果您可以证明算法是最佳的,那么无价的布朗尼积分。


废话
少说

18
@ zyabin101您刚刚失去了巧克力蛋糕的机会。
加尔文的爱好

9
您总是拿出如此出色的标题!
路易斯·门多

@HelkaHomba-._(._.)_.-
user48538 '16

对于N=3最佳情况,您包括了可能的输出吗?
R. Kap

Answers:


9

Pyth 96 94字节

Mt*Q+++bGdHM|%+y_GHQQg1 2++Qd1g2 3g2 1g3 1++Qd2Vr3QgNtN++QdN;g1QVStQVStQI<NHgnNHnNtH)++nN0dnNH

在这里尝试

它是如何工作的?

该解释将使用N = 5。

第1部分:在每个堆栈上创建底层

之所以需要单独的代码,是因为每个堆栈都需要使用:前4个堆栈需要在其下方放置一个5,最后一个堆栈必须提供5个堆栈。这意味着我们不能只将所有的4s移动到某个地方,在其中放置5,然后再将4s移回去。

可视化:(括号表示要移动的内容)

     _
11111 |
22222 |_ Can't move 4s here, not monotonically increasing
33333_|
(44444)------------??? Where to put the 4s?
55555 <- Must supply the 5 that will be moved

相反,要进行第一次交换,我们首先将所有1移到第二个堆栈,将5移到第一个堆栈(现在为空),将1移到第三个堆栈,将2移到第一个堆栈堆栈,将1移回第一个堆栈,最后将5移至第二个堆栈。

(11111)-----.
2222211111<-'
===============================
5<---------.
2222211111 : (from stack 5)
===============================
5
22222(11111)-.
3333311111<--'
===============================
522222<-.
(22222)-'
3333311111
===============================
52222211111<-.
             |
33333(11111)-'
===============================
52222211111
5<-----.
33333  |
44444  |
555(5)-'

现在我们有了一个自由空间来将堆栈移动到其中(堆栈2,其中只包含一个放置在正确位置的5),我们可以将所有3移到堆栈2并将5放入堆栈3。然后可以重复对于堆栈4同样如此,现在我们将所有5放在正确的位置!还有一件事情:我们将所有1移到堆栈5,以便为下一次堆栈交换提供一个不错的设置。

522222(11111)-.
533333        |
544444        |
5             |
511111<-------'

第2部分:做其他事情:)

现在这要容易得多,因为现在我们将始终有一个免费堆栈来移动我们需要处理的其他数字。因此,首先我们要弄清楚4在哪里。稍微检查一下,就会发现它始终从开始处向上1,或者在最后一个堆栈上方2。现在,我们只是继续往下走,如果空闲的话,将4放在堆栈中,否则将其他数字向上移动1。现在,我们已将所有4设置到位。

522222<------.
533333<----. |
544444-.-.-'-'
5<-----' |
511111<--'
===============================
5433333
54
54
5411111
5422222

现在,我们意识到3s在4s所在的位置之上是2个堆栈。这意味着我们可以做与4s完全相同的事情!事实证明,只要将堆栈索引包装到另一侧,我们就可以继续这样做。

5433333-'wrap around 543
54                   543
54                   54311111
5411111 .----------->54322222
5422222 |2 stacks up 543

因此,我们可以继续这样做,直到交换完所有堆栈为止。

代码说明:

首先:(重要的)预定义变量。

Q: Evaluated input.
b: The newline character, '\n'
d: A space, ' '

有2个lambda定义。

M           | g(G)(H), used for moving Q numbers at a time.
            | We will call these Q numbers a "(number) block"
 t          | Tail, used to remove beginning newline
  *Q        | Repeat the following Q times
    +++bGdH | '\n' + G + ' ' + H. Just a whole bunch of concatenating.
            |
M           | n(G)(H), used for figuring out which stacks to move from
 |       Q  | If the following code is 0 (false), then use Q instead
  %     Q   | Mod Q
   +   H    | Add H
    y       | Multiply by 2
     _G     | Negate (remember in the explanation part 2? Always 2 stacks above?)

堆栈交换:第1部分

g1 2                       | Move the 1 block to stack 2
    ++Qd1                  | Move a Q to stack 1
         g2 3              | Move the 1 block to stack 3
             g2 1          | Move the 2 block to stack 1
                 g3 1      | Move the 1 block back to stack 1
                     ++Qd2 | Move a Q to stack 2
 v---Code-continuation---' |I don't have enough room!!!
Vr3Q                       | For N in range(3, Q)
    gNtN                   | Move the number block in stack N up 1
        ++QdN              | Move a Q to stack N
             ;g1Q          | End for loop; move the 1 block to the last stack

堆栈交换:第2部分

VStQ                           | For N in [1, 2, ..., Q - 1]
    VStQ                       | For H in [1, 2, ..., Q - 1]
        I<NH                   | If N < H
            g                  | Number block move
             nNH               |  (find number block)
                nNtH           |  (find the previous stack)
                    )          | End "For H"
                     ++nN0dnNH | Find start, move number to next location down

我已经知道我没有得到布朗尼积分,因为我可以看到许多更有效,更复杂的方法:(

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.