数学思想——归纳推理(不是反证法)
为了方便,我把塔叫做牌,最左边的是从大到小(底部开始)放置的的牌堆。
数字的那一列是递归调用,右边长度不一的箭头是,数字阶段向下调用方法的情况(每一次向下指的线都是一次发牌)。发牌次数加上第一次n == 1(局部变量)的那一张牌刚好就是牌堆里面的总数N张牌。
在网上找到的代码
#include<stdio.h> //递归找
#include<math.h>
int Hanoi_count(int n)
{
return pow(2, n) - 1;
}
void move(int x, int y)
{
printf("%c->%c\n", x, y);
}
void Hanoi(int n, char a, char b, char c)
{
if (n == 1)
{
move(a, c);
}
else
{
Hanoi(n - 1, a, c, b);//将A座上的n-1个盘子借助C座移向B座
move(a, c);//将A座上最后一个盘子移向C座
Hanoi(n - 1, b, a, c);//将B座上的n-1个盘子借助A座移向C座
}
}
//move中的实参与hanoi函数中的形参相对应,而hanoi函数中形参a,b,c所对应的值也是在有规律的变化
int main()
{
int n = 0;
scanf("%d", &n);
int ret = Hanoi_count(n);
printf("%d层汉诺塔完成移动一共要%d步\n", n, ret);
Hanoi(n, 'A', 'B', 'C');
return 0;
}
输入1 2 3,每次第一次移动牌都会变,不是2就是3,我们从这里可以归纳出规律来——每次不同牌堆数不同的放置调整总是放到B或者C区域,可能和牌堆的数量有关。
尝试去证明——一张牌,直接放在了C区域。两张牌则是B区域开始,三张又是C区域开始了。
首先绝对不可能自己发到自己,所以只能发到B区域或者C区域。发牌区发牌的时候,两个区域都是调整区域。一张牌的时候放在C区域,2张若第一张放B区域了,2张的第二张就是C区域了,B区域的那张牌只能放在C区域的第一张牌上面了,不然就只能按大到小给放回牌堆了,或者无意义的乱。这里出现的矛盾,因此牌堆2时只能先放在B,3张牌若和2一样就也会出现矛盾,以此类推,牌堆1线性增加,每次放第一张牌的时候都是B、C、B、C区域的规律放置第一张牌的。(还可以这样解释,堆起来空起来的位置可以是要目标大盘放置的位置,只是没有物理的把盘子给交换,所以只需要调整位置即可,圆盘 A B C,C堆起来了,把B和C交换位置A C B,如果不看标记,把A的牌堆第一张牌放到大盘B上,C再借A再放一次自己的造型到B上,就是牌堆数量为n + 1的情况了)
上面的证明,其实已经证明了放一个,再放一堆的方法了的有效性了。
但是还是希望能注意到——a、b、c不是是A、B、C区域,只是一个局部变量。
void Hanoi(int n, char a, char b, char c)
{
if (n == 1)
{
move(a, c);
}
else
{
Hanoi(n - 1, a, c, b);//将A座上的n-1个盘子借助C座移向B座
move(a, c);//将A座上最后一个盘子移向C座
Hanoi(n - 1, b, a, c);//将B座上的n-1个盘子借助A座移向C座
}
}
Hanoi(n - 1, a, c, b);
这个a 就是A ,但是c和b就会B C、C B、C B、B C的规律变化
Hanoi(n - 1, b, a, c);
去掉中间的第二个字母,就是a到b或者b到c (b 可以是 B或者C)
move(a, c);//将A座上最后一个盘子移向C座
Hanoi(n - 1, b, a, c);//将B座上的n-1个盘子借助A座移向C座
就是发新牌,然后在把现在堆起来的新牌群的牌的造型又堆在新发的牌的上面
至于为什么
Hanoi(n - 1, a, c, b);//将A座上的n-1个盘子借助C座移向B座
move(a, c);//将A座上最后一个盘子移向C座
Hanoi(n - 1, b, a, c);//将B座上的n-1个盘子借助A座移向C座
这里为什么n - 1最后一次可以直接一堆在C上还要
Hanoi(n - 1, a, c, b);//将A座上的n-1个盘子借助C座移向B座
这里的n - 1出现的原因是为了递归 n - 1传到下面的n ,下面的n == n - 2,以此类推才能n == 1