问题:有三个柱子放在左中右三个位置,左边柱子上有n个碟子(都不一样大)从上到下按从小到大的顺序叠起来放置,最底下是第n个碟子,最大。现要将n个碟子从左边柱子移动到右边柱子。要求:每次只能移动一个碟子;大碟子不能压在小碟子上;只能往相邻的柱子移动。求移动碟子的最少次数,并输出移动次数最少时的每个移动过程。
n个碟子的问题可以这么考虑:先将n-1个碟子从左柱子依次移动到中间、右边的柱子,然后将最底下的碟子移到中间,再将n-1个碟子移动回左边,第n个碟子移到右边。如此一来,规模为n的问题就变成了n-1规模的问题,可以采用递归进行求解。
#include
#include
using std::string;
using std::cout;
using std::endl;
//用于打印每个移动步骤,num为碟子序号,origin为移动前所在的柱子,dest为移动后所在的柱子
void printStep(int num, string origin, string dest) {
cout << "Move " << num << " from " << origin << " to " << dest << endl;
}
printStep用于打印每个移动步骤,注意由于移动只能发生在相邻的柱子之间,此函数的发出地,终点也应该是相邻的
//将num个碟子从origin移动到dest的函数,left,mid,right为左中右三个柱子的名称
//函数返回移动的总次数
int process(int num, string left, string mid, string right, string origin, string dest) {
//假设输入的出发点和终点位置不同
//递归基,如果只有一个碟子,直接将其移动到目的柱子处即可
if (num == 1) {
if (origin == mid || dest == mid) {
printStep(num, origin, dest);
return 1;
}
else
{
printStep(num, origin, mid);
printStep(num, mid, dest);
return 2;
}
}
int sum = 0;//用于记录移动总次数
//移动分多种情况,从mid移动到两边,从两边移动到中间,或者从一端移动到另一端
if (origin == mid) {
//从mid移动到某一端时,先将最上面的n-1个移动到dest对面的另一端,记为another
string another = (dest == left ? right : left);
sum += process(num - 1, left, mid, right, mid, another);
//然后将第n个碟子移到目的地
printStep(num, origin, dest);
//最后将处于另一端的n-1个碟子移到目的地
sum += process(num - 1, left, mid, right, another, mid);
sum += process(num - 1, left, mid, right, mid, dest);
}
else if (dest == mid) {
//如果从某一端移动到中间柱子,同理,先将n-1个碟子移到另一端,再将第n个碟子移到中间,最后将n-1个碟子移到中间
string another = (origin == left ? right : left);
sum += process(num - 1, left, mid, right, origin, dest);
sum += process(num - 1, left, mid, right, dest, another);
printStep(num, origin, dest);
sum += process(num - 1, left, mid, right, another, dest);
}
else {
//从一端移到另一端
sum += process(num, left, mid, right, origin, mid);
sum += process(num, left, mid, right, mid, dest);
}
return sum;
}
最后的HanoiTower1函数,利用了process函数进行递归。
int HanoiTower1(int num, string left, string mid, string right) {
if (num < 1)
return 0;
else if (num == 1) {
printStep(num, left, mid);
printStep(num, mid, right);
return 2;
}
int sum = 0;
//前n-1个碟子移动到右柱子
sum += process(num - 1, left, mid, right, left, mid);
sum += process(num - 1, left, mid, right, mid, right);
//第n个碟子移到中间
printStep(num, left, mid);
//前n-1个碟子移动到左柱子
sum += process(num - 1, left, mid, right, right, mid);
sum += process(num - 1, left, mid, right, mid, left);
//第n个碟子移到右边
printStep(num, mid, right);
sum += 2;//加上两次第n个碟子的移动
//化为了n-1规模的子问题
sum += HanoiTower1(num - 1, left, mid, right);
return sum;
}
主函数
int main()
{
int x=HanoiTower1(2, "L", "M", "R");
cout << "Total steps num is: " << x << endl;
return 0;
}
递归部分写得比较啰嗦,为了便于理解,其实优化后的代码HanoiTower1函数可以写得很简单。
引用自《程序员代码面试指南》