首先我们先回忆一下经典的汉诺塔问题:
问题描述
相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
既然都介绍了,我们就来写一下这个三柱汉诺塔问题的一个核心代码吧
void move(int i,int j)
{
cout << i << " -> " << j << endl;
num ++ ;
}
void hanoi(int n,int a,int b,int c)
{
if(n == 0)
return;
else{
hanoi(n - 1,a,c,b);
move(a,c);
hanoi(n - 1,b,a,c);
}
}
好了,我们介绍一下四柱汉诺塔问题。其实问题很简单,就是再加一根柱子,把A上的盘子,经过B和C移动到D上去。
分析
将k个盘子先从A上经过C,D移动到B上去,然后再将下面n-k个盘子经过C移动到D上去,
再将之前的k个盘子经过A,C移动到D上去,完成。
我们采用动态规划的思想
dp[i]表示当盘子数为 i时最小的移动次数
最优子结构:
dp[i] = pow(2, i - j) - 1 + 2 * dp[j];从j处断开 j <= i
即先把j个盘子做一次四柱汉诺塔,再把i-j个盘子做一次三柱汉诺塔,再把j个盘子做一次四柱汉诺塔。
(其中pow(2, i - j) - 1的意思是i-j个盘子用三个柱子移动时的次数,是固定的)
/*
n个盘子的四柱汉诺塔问题
*/
#include
#include
#include
#include
#define MAX_N 63
#define INF 0x3f3f3f3f
using namespace std;
int dp[MAX_N];
int num;
void move(char a,char b)
{
printf("%c --> %c\n", a, b);
num++;
}
int find_hanoi(int n,int &k)
{
num = 0;
dp[1] = 1;
dp[2] = 3;
for (int i = 3; i <= n; i++)
{
for (int j = 1; j < i; j++)
{
long long t = pow(2, i - j) - 1 + 2 * dp[j];
if (t < dp[i] || !dp[i])
{
dp[i] = t;
k = j;
}
}
}
return dp[n];
}
int main()
{
int n;
int k = 0;
cout << "请输入盘子的数目n" << endl;
memset(dp, 0, sizeof(dp));
cin >> n;
cout << "移动" << n << "个盘子最少需要的次数为:" << find_hanoi(n, k) << endl;
cout << "断开位置:" << k << endl;
return 0;
}
下面的代码就加了一个内容,输出移动次序,并且验证结果是否正确。
其中我们利用了动态规划的算法,计算出了n个盘子的情况下最优的断开位置
/*
n个盘子的四柱汉诺塔问题
*/
#include
#include
#include
#include
#define MAX_N 63
#define INF 0x3f3f3f3f
using namespace std;
int dp[MAX_N];
int num = 0;
int myfind[MAX_N];
int find_hanoi(int n)
{
num = 0;
dp[1] = 1;
dp[2] = 3;
for (int i = 3; i <= n; i++)
{
for (int j = 1; j < i; j++)
{
long long t = pow(2, i - j) - 1 + 2 * dp[j];
if (t < dp[i] || !dp[i])
{
dp[i] = t;
myfind[i] = j;
}
}
}
return dp[n];
}
void move(char i,char j)
{
cout << i << " -> " << j << endl;
num ++ ;
}
void hanoi03(int n,int a,int b,int c)
{
if(n == 0)
return;
else{
hanoi03(n - 1,a,c,b);
move(a,c);
hanoi03(n - 1,b,a,c);
}
}
void hanoi04(int n, int k,int a,int b,int c,int d)
{
if(n == 1){
move(a,d);
return;
}
if(n == 2){
move(a,c);
move(a,d);
move(c,d);
return;
}
if(k == 0)
return;
hanoi04(k,myfind[k],a,c,d,b);
hanoi03(n-k,a,c,d);
hanoi04(k,myfind[k],b,a,c,d);
}
int main()
{
int n;
int k = 0;
char a = 'A';
char b = 'B';
char c = 'C';
char d = 'D';
memset(dp, 0, sizeof(dp));
memset(myfind,0,sizeof(myfind));
cout << "请输入盘子的数目n" << endl;
cin >> n;
int index = find_hanoi(n);
cout << "移动" << n << "个盘子最少需要的次数为:" << index << endl;
cout << "移动次序****" << endl;
hanoi04(n,myfind[n],a,b,c,d);
if(num == index){
cout << "验证正确" << endl;
}
return 0;
}