经典算法问题1:汉诺塔问题【递归】

经典汉诺塔问题

汉诺塔问题是经典的可以用递归解决的问题。

汉诺塔(Hanoi)游戏规则如下:在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。计算需要移动的次数。

我们先来对汉诺塔的步数进行一下递推。

对于 n n n个盘子,我们可以把它分成 n − 1 n-1 n1个盘子和最后一个大盘子。设 F ( n ) F(n) F(n)为移动所需步数,那么对于 n n n个盘子来说所做的事情就是

  1. n − 1 n-1 n1个盘子从A柱借助C柱移动到B柱,这一过程移动的步数为 F ( n − 1 ) F(n-1) F(n1)
  2. 将大盘子从A柱直接移动到C柱,此时需要1步。
  3. n − 1 n-1 n1个盘子从B柱借助A柱移动到C柱,此时需要的步数仍为 F ( n − 1 ) F(n-1) F(n1)

综合以上分析, F ( n ) = 2 F ( n − 1 ) + 1 F(n)=2F(n-1)+1 F(n)=2F(n1)+1。对两边同时加上1可以凑成一个等比数列,然后就可以求出其通项公式。

完整C++代码:

#include <iostream>
#include <cmath>
using namespace std;

void Hanoi(char a, char b, char c, int n) // 把n个盘从a经过b移动到c
{
  if (n == 0)
    return;
  Hanoi(a, c, b, n - 1);
  cout << a << " -> " << c << endl;
  Hanoi(b, a, c, n - 1);
}
int main()
{
  int n;
  cin >> n;
  Hanoi('a', 'b', 'c', n);
}

如果要计算移动的次数可以定义一个全局变量ans来保存:

#include <iostream>
#include <cmath>
using namespace std;
int ans = 0;
void Hanoi(char a, char b, char c, int n) // 把n个盘从a经过b移动到c
{
  if (n == 0)
    return;
  Hanoi(a, c, b, n - 1);
  ans++;
  cout << a << " -> " << c << endl;
  Hanoi(b, a, c, n - 1);
}
int main()
{
  int n;
  cin >> n;
  Hanoi('a', 'b', 'c', n);
  cout << ans;
}

输入:3
输出:
在这里插入图片描述

改后的汉诺塔问题

改后的汉诺塔问题如下:

汉诺塔(Hanoi)游戏规则如下:在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,且限定圆盘只能够移动到相邻的柱子,即A柱子上的圆盘只能够移动到B,B柱子上的圆盘只能够移动到A或者C,C同理。并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。计算需要移动的次数。

我们先来对改后的汉诺塔的步数进行一下递推。

对于 n n n个盘子,我们还是可以把它分成 n − 1 n-1 n1个盘子和最后一个大盘子。设 F ( n ) F(n) F(n)为移动所需步数,那么对于 n n n个盘子来说所做的事情就是

  1. n − 1 n-1 n1个盘子从A柱借助B柱移动到C柱,这一过程移动的步数为 F ( n − 1 ) F(n-1) F(n1)
  2. 将大盘子从A柱直接移动到B柱,此时需要1步。
  3. n − 1 n-1 n1个盘子从C柱借助B柱移动到A柱,此时需要的步数仍为 F ( n − 1 ) F(n-1) F(n1)
  4. 将大盘子从B柱直接移动到C柱,此时需要1步。
  5. n − 1 n-1 n1个盘子从A柱借助B柱移动到C柱,此时需要的步数仍为 F ( n − 1 ) F(n-1) F(n1)

综合以上分析, F ( n ) = 3 F ( n − 1 ) + 2 F(n)=3F(n-1)+2 F(n)=3F(n1)+2。对两边同时加上1可以凑成一个等比数列,然后就可以求出其通项公式。

完整C++代码:

#include <iostream>
#include <cmath>
using namespace std;

void Hanoi(char a, char b, char c, int n) // 把n个盘从a经过b移动到c
{
  if (n == 0)
    return;
  Hanoi(a, b, c, n - 1);
  cout << a << " -> " << b << endl;
  Hanoi(c, b, a, n - 1);
  cout << b << " -> " << c << endl;
  Hanoi(a, b, c, n - 1);
}
int main()
{
  int n;
  cin >> n;
  Hanoi('a', 'b', 'c', n);
}

如果要计算移动的次数可以定义一个全局变量ans来保存:

#include <iostream>
#include <cmath>
using namespace std;
int ans = 0;
void Hanoi(char a, char b, char c, int n) // 把n个盘从a经过b移动到c
{
  if (n == 0)
    return;

  Hanoi(a, b, c, n - 1);
  ans++;
  cout << a << " -> " << b << endl;
  Hanoi(c, b, a, n - 1);
  ans++;
  cout << b << " -> " << c << endl;
  Hanoi(a, b, c, n - 1);
}
int main()
{
  int n;
  cin >> n;
  Hanoi('a', 'b', 'c', n);
  cout << ans;
}

输入:3
输出:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值