Skip to content

T01958: Strange Towers of Hanoi

http://cs101.openjudge.cn/practice/01958/

Charlie Darkbrown sits in another one of those boring Computer Science lessons: At the moment the teacher just explains the standard Tower of Hanoi problem, which bores Charlie to death!

img

The teacher points to the blackboard (Fig. 4) and says: "So here is the problem:

  • There are three towers: A, B and C.
  • There are n disks. The number n is constant while working the puzzle.
  • All disks are different in size.
  • The disks are initially stacked on tower A increasing in size from the top to the bottom.
  • The goal of the puzzle is to transfer all of the disks from tower A to tower C.
  • One disk at a time can be moved from the top of a tower either to an empty tower or to a tower with a larger disk on the top.

So your task is to write a program that calculates the smallest number of disk moves necessary to move all the disks from tower A to C." Charlie: "This is incredibly boring—everybody knows that this can be solved using a simple recursion.I deny to code something as simple as this!" The teacher sighs: "Well, Charlie, let's think about something for you to do: For you there is a fourth tower D. Calculate the smallest number of disk moves to move all the disks from tower A to tower D using all four towers." Charlie looks irritated: "Urgh. . . Well, I don't know an optimal algorithm for four towers. . . " Problem So the real problem is that problem solving does not belong to the things Charlie is good at. Actually, the only thing Charlie is really good at is "sitting next to someone who can do the job". And now guess what — exactly! It is you who is sitting next to Charlie, and he is already glaring at you. Luckily, you know that the following algorithm works for n <= 12: At first k >= 1 disks on tower A are fixed and the remaining n-k disks are moved from tower A to tower B using the algorithm for four towers.Then the remaining k disks from tower A are moved to tower D using the algorithm for three towers. At last the n - k disks from tower B are moved to tower D again using the algorithm for four towers (and thereby not moving any of the k disks already on tower D). Do this for all k ∈{1, .... , n} and find the k with the minimal number of moves. So for n = 3 and k = 2 you would first move 1 (3-2) disk from tower A to tower B using the algorithm for four towers (one move). Then you would move the remaining two disks from tower A to tower D using the algorithm for three towers (three moves). And the last step would be to move the disk from tower B to tower D using again the algorithm for four towers (another move). Thus the solution for n = 3 and k = 2 is 5 moves. To be sure that this really is the best solution for n = 3 you need to check the other possible values 1 and 3 for k. (But, by the way, 5 is optimal. . . )

输入

There is no input.

输出

For each n (1 <= n <= 12) print a single line containing the minimum number of moves to solve the problem for four towers and n disks.

样例输入

No input.

样例输出

REFER TO OUTPUT.

来源

TUD Programming Contest 2002, Darmstadt, Germany

《短码之美》2007年,184页

汉诺塔,大家知道吗?汉诺塔由 3根柱子、大小不同的空心圆盘组成。所有圆盘最初都放在最左边的柱子上。圆盘的摆放规则是上面的圆盘必须小于下面的圆盘。把这些圆盘一个一个都移动到最右边的柱子上,如果圆盘的个数是 n,大家都知道一般需要移动 (2^n^-1)次。比如,n=3的时候,

image-20231030193822757

的确是用了 2^3^-1=7 次完成了移动。那么,这次的问题不是基本的汉诺塔,而是把柱子的根数增加1根。如果柱子增加到 4根,原来需要移动 7次完成,现在只需要 5次就可以了。

image-20231030194009343

如果增加圆盘个数,就应该能省下更多的步数,但是这个规则还不是很清楚。题面要求编写程序计算 4根柱子的时候,1~12 个盘子所需的最小移动次数。

有 4根柱子的时候,可以利用2根空的柱子移动圆盘,圆盘数 n是 1、2、3的时候只需顺序移动,所以各需要 1、3、5次移动。4个圆盘以上: (1)首先移动其中的几个盘子; (2)把剩余的圆盘移动到指定的位置; (3)把(1)的圆盘移动到(2)的上面。 这个时候,(1)和(3)可以有 2 根空柱子可以使用,所以可以互换,但是(2)的时候只有一根空柱子。也就是说移动所需的步数与一般汉诺塔 (3 根柱子)相同。

image-20231030194457296

具体地用 4个圆盘来考虑一下,如下图所示。4个圆盘的时候,①可移动2个圆盘 (3步),②可移动2个圆盘 (3步),③再移动2个圆盘 (3步),总共最少需要 9步。如果①移动3个的时候,则需要 5步,②只移动一个需要 1步,③再移动3 个需要 5步,总共需要 11 步,不是最小的移动步数。但是,①只移动1个的话需要 1步,②只移动3个需7步,③再移动 1个需要1步,总共需要 9步,这才是最小步数。在什么情况下移动步数最小不太容易看出来,所以要像这样把 n个圆盘分成k个和 (n-k) 个来检查移动步数,找出最小移动步数的移法。

image-20231030195445757

圆盘个数增加后需要增加移动步数,如果每次都计算将是很庞大的计算量,所以需要使用DP(Dynamic Programming,动态规划法)求解。

Frame-Stewart 算法,分治+动归

python
d = [0] * 15
f = [float('inf')] * 15

d[1] = 1
for i in range(2, 13):
    d[i] = d[i - 1] * 2 + 1

f[1] = 1
for i in range(2, 13):
    for j in range(1, i):
        f[i] = min(f[i], f[i - j] * 2 + d[j])

for i in range(1, 13):
    print(f[i])

题目大意就是要求你解出n个盘子4座塔的Hanoi问题的最少步数,不需要输入,直接输出n为1-12的所有答案即可。我们知道,一般的三塔Hanoi问题的递推式是d[i]=d[i-1]*2+1,意思就是先将i-1个盘子放在第二个塔上,再把最后一个放在第三个塔上,再将i-1个盘子放在第三个塔上,当然这种方法实质上是将i个盘子的问题先转化为i-1个盘子的问题。那么做这题就可以用类似的思维,先将i个盘子的四塔问题转化为i-j个盘子的四塔问题(1<=j<i),令f[i-j]i-j个盘子的四塔问题的答案,则f[i]=min(f[i],f[i-j]*2+d[j])。实际上也就等效于先做i-j个盘子的四塔问题,再做j个盘子的三塔问题,再做一次i-j个盘子的四塔问题。

python
# 23n2300011072,蒋子轩
def hanoi_four_towers(n, source, target, auxiliary1, auxiliary2):
    if n == 0:
        return 0
    if n == 1:
        return 1
    min_moves = float('inf')
    for k in range(1, n):
        three_tower_moves = 2**(n-k)-1
        moves = hanoi_four_towers(k, source, auxiliary1, auxiliary2, target) +\
            three_tower_moves +\
            hanoi_four_towers(k, auxiliary1, target, source, auxiliary2)
        min_moves = min(min_moves, moves)
    return min_moves


for n in range(1, 13):
    print(hanoi_four_towers(n, 'A','D','B','C'))