【数据结构】基于递归策略解决汉诺塔问题及其变体

汉诺塔问题是一种经典的问题。常见的处理方法是使用递归策略来处理。下面我们来介绍汉诺塔问题,及其两种变体。


1. 汉诺塔 I

1.1 问题描述

有一座塔A,上有64个碟。所有碟子按从大到小的次序从塔底堆放至塔顶。紧挨着塔A有另外两个塔B和塔C。
问题是:如何借助塔B,将塔A上的碟子移动到塔C上去,每次只能移动一个碟子,任何时候都不能把一个碟子放在比它小的碟子上面。
现在有N个圆盘,至少进行多少次移动才能把这些圆盘从最左边移动到最右边。

1.2 解决方案

采用递归策略解决问题,若从第一根杆上移动N个圆盘到第三根杆上需要F[N]次移动,那么F[N]的组成方式如下:

1. 先移动N-1个圆盘到第二根杆上需要F[N-1]次 移动

2. 将最大的圆盘移动到第三根杆上需要1次移动

3. 最后移动N-1个圆盘到第三根杆上需要F[N-1]次 移动

综上所述,有F[N] = 2 * F[N-1] + 1

1.3 代码

//汉诺塔问题
/*
有一座塔A,上有64个碟。所有碟子按从大到小的次序从塔底堆放至塔顶。紧挨着塔A有另外两个塔B和塔C。
问题是:如何借助塔B,将塔A上的碟子移动到塔C上去,每次只能移动一个碟子,任何时候都不能把一个碟子放在比它小的碟子上面。
现在有N个圆盘,至少进行多少次移动才能把这些圆盘从最左边移动到最右边。
*/
#include<iostream>
#include<cstdio>

using namespace std;

long long Function(int n)
{
	if (n == 1) return 1;
	else
	{
		return 2 * Function(n - 1) + 1;
	}
}

int main()
{
	int n;
	while (scanf("%d", &n) != EOF)
	{
		if (n == 0) break;
		cout << "Move : " << Function(n) << endl;
	}

	return 0;
}

2. 汉诺塔 II

2.1 问题描述

有一座塔A,上有64个碟。所有碟子按从大到小的次序从塔底堆放至塔顶。紧挨着塔A有另外两个塔B和塔C。
问题是:如何借助塔B,将塔A上的碟子移动到塔C上去,每次只能移动一个碟子,任何时候都不能把一个碟子放在比它小的碟子上面。
现在引入第4座塔D,上述规则保持不变。若有N个圆盘,至少进行多少次移动才能把这些圆盘从A移动到C?

2.2 解决方案

这是一个多柱汉诺塔问题,若总共移动i个碟子,具体步骤如下:

1. 将j个盘从a柱移动到c柱(借助b,d柱),所需步数为F[j]

2. 将a柱剩下的(i-j)个盘移动到d柱(借助b柱),是最基础的汉诺塔问题,可证明所需步数为2^(i-j)-1

3. 将c柱上的j个盘移动到d柱(借助a,b柱),所需步数为F[j]

综上所述,有F[i] = 2 * F[j] + 2^(i-j)-1.

显然上述结果随着j变化,若求最小的步数,则答案应为F[i] = min( 2 * F[j] + 2^(i-j)-1 ) (0<j<i),我们可以采用for循环来实现

2.3 代码

//汉诺塔问题 II
/*
有一座塔A,上有64个碟。所有碟子按从大到小的次序从塔底堆放至塔顶。紧挨着塔A有另外两个塔B和塔C。
问题是:如何借助塔B,将塔A上的碟子移动到塔C上去,每次只能移动一个碟子,任何时候都不能把一个碟子放在比它小的碟子上面。
现在引入第4座塔D,上述规则保持不变。若有N个圆盘,至少进行多少次移动才能把这些圆盘从A移动到C?
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>

using namespace std;

const int N = INT_MAX;

void Function(long long data[])
{
	data[1] = 1;
	data[2] = 3;
	long long min, number;
	for (int i = 3; i < 65; i++)
	{
		min = N;
		for (int j = 1; j < i; j++)
		{
			number = 2 * data[j] + (int)pow(2, i - j) - 1;
			if (number < min) 
				min = number;
		}
		data[i] = min;
	}

	return;
}

int main()
{
	int n;
	long long data[65];
	Function(data);

	while (scanf("%d", &n) != EOF)
	{
		if (n == 0) break;
		cout << "Move : " << data[n] << endl;
	}

	return 0;
}

3. 汉诺塔 III

3.1 问题描述

有一座塔A,上有64个碟。所有碟子按从大到小的次序从塔底堆放至塔顶。紧挨着塔A有另外两个塔B和塔C。
问题是:如何借助塔B,将塔A上的碟子移动到塔C上去,每次只能移动一个碟子,任何时候都不能把一个碟子放在比它小的碟子上面。
现改变规则:不允许直接从最左边移动到最右边(每次移动一定是移到中间杆或从中间杆移出),也不允许大圆盘放到小圆盘的上面。
若有N个圆盘,至少进行多少次移动才能把这些圆盘从A移动到C?

3.2 解决方案

采用递归策略解决问题,若从第一根杆上移动N个圆盘到第三根杆上需要F[N]次移动,那么F[N]的组成方式如下:

1. 先移动N-1个圆盘到第二根杆上需要F[N-1]次 移动

2. 将最大的圆盘移动到中间杆上需要1次移动

3. 再移回N-1个圆盘到第一根杆上需要F[N-1]次 移动

4. 将最大的圆盘移动到最后一根杆上需要1次移动

5. 最后移动N-1个圆盘到第三根杆上需要F[N-1]次 移动

综上所述,有F[N] = 3 * F[N-1] + 2

3.3 代码

//汉诺塔问题 III
/*
有一座塔A,上有64个碟。所有碟子按从大到小的次序从塔底堆放至塔顶。紧挨着塔A有另外两个塔B和塔C。
问题是:如何借助塔B,将塔A上的碟子移动到塔C上去,每次只能移动一个碟子,任何时候都不能把一个碟子放在比它小的碟子上面。
现改变规则:不允许直接从最左边移动到最右边(每次移动一定是移到中间杆或从中间杆移出),也不允许大圆盘放到小圆盘的上面。
若有N个圆盘,至少进行多少次移动才能把这些圆盘从A移动到C?
*/

#include<iostream>
#include<cstdio>

using namespace std;

long long Function(int n)
{
	if (n == 1) return 2;
	else
	{
		return 3 * Function(n - 1) + 2;
	}
}

int main()
{
	int n;
	while (scanf("%d", &n) != EOF)
	{
		if (n == 0) break;
		cout << "Move : " << Function(n) << endl;
	}

	return 0;
}

4. 总结

上述问题,对于借助递归策略解决问题是一个很好的锻炼,以后也要时常回顾。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值