-
汉诺塔
(益智玩具)
汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根
金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
一、代码实现:
//汉诺塔实现,塔为A B C
#include<bits/stdc++.h>
using namespace std;
void hanoi(int n,char a,char b,char c)
{
if(n==1)
cout<<"将第"<<n<<"个从 "<<a<<" -> "<<c<<endl;
else{
hanoi(n-1,a,c,b);
cout<<"将第"<<n<<"个从 "<<a<<" -> "<<c<<endl;
hanoi(n-1,b,a,c);
}
}
int main()
{
int n;
while(cin>>n){
char a='A',b='B',c='C';
hanoi(n,a,b,c);
}
return 0;
}
结果:
二、递推得出移动的最小次数:
假设棒子为A,B,C,A上原本有n个,假设次数为f(n)。递推的步骤为:A上的n-1个从A移到B(通过C,所以等价于n-1个圆盘通过B从A移到C,为f(n-1)),A上第n个直接从A移到C,n-1个圆盘通过A从B移到C,次数也为f(n-1),所以递推公式为f(n)=2*f(n-1)+1;f(n)=2^n-1;
三、例题:
(一)、hdu1207(汉诺塔Ⅱ)
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1207
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ll res[80];
res[1]=1;
res[2]=3;
for(int i=3;i<=64;i++){
ll mintemp=1000000000;
for(int j=1;j<i;j++){
if(mintemp>2*res[j]+pow(2,i-j)-1)
mintemp=2*res[j]+pow(2,i-j)-1;
}
res[i]=mintemp;
}
ll n;
while(cin>>n){
cout<<res[n]<<endl;
}
return 0;
}
这道题是借助四个柱子来实现汉诺塔,分析如下:
假设存在A,B,C,D个柱子,A上有n个圆盘,目标是将A上的所有圆盘移到D上(中途按照一定的规则),假设最小移动步数为f(n),那么最佳的移动方法是:
将A上的柱子分为两批,数量为r和n-r(r在上方)。
首先r通过C和D移到B,步数为f(n-1)
其次n-r个圆盘只能通过C移动到D,像不像最初的汉诺塔问题,所以步数为2^(n-r)-1
紧接着r个圆盘通过A和C从B移动到D,步数为f(r);
递推公式为f(n)=2*f(n-1)+2^(n-r)-1;
但是由于不知道何处取到最小值,用dp了。
(二)、hdu2064(汉诺塔Ⅲ)
:
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2064
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ll res[36];
res[1]=2;
for(int i=2;i<=35;i++)
res[i]=3*res[i-1]+2;
int n;
while(cin>>n){
cout<<res[n]<<endl;
}
return 0;
}
分析如下:
假设如上几题所示:
第一步:n-1个圆盘通过B从A移到C,步数为f(n-1)
第二步:第n个圆盘从A移动到B,一步
第三步:n-1个圆盘通过B从C移动到A,步数为f(n-1)
第四步:第n个圆盘从B移动到C,一步
第五步:n-1个圆盘通过B,从A移动到C,步数为f(n-1)
递推公式为:f(n)=3*f(n-1)+2;
(三)、hdu2077(汉诺塔Ⅳ)
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=2077
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
ll F[30],f[30];
F[1]=2;
for(int i=2;i<=20;i++)
F[i]=3*F[i-1]+2;
f[1]=2;
for(int i=2;i<=20;i++)
f[i]=F[i-1]+2;
int ncase;
cin>>ncase;
while(ncase--){
int n;
cin>>n;
cout<<f[n]<<endl;
}
return 0;
}
分析:
和汉诺塔Ⅲ类似,只要判断最后一个圆盘的情况,在n-1个圆盘移动的过程中,只需在某一时刻将最大的圆盘从A移动到B,从B移动到C便可。
所以公示为:f(i)=F(i-1)+2;
F(i)=3*F(i-1)+2;
(四)、汉诺塔Ⅴ
说实话,我是猜答案的,感觉有这样的规律,没有去证明:
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll fast_pow(ll a,ll b)
{
ll temp=1;
while(b){
if(b&1)
temp=temp*a;
a=a*a;
b>>=1;
}
return temp;
}
int main()
{
ll ncase;
cin>>ncase;
while(ncase--){
ll n,m;
cin>>n>>m;
cout<<fast_pow(2,n-m)<<endl;
}
return 0;
}
(五)、汉诺塔
Ⅵ
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1996
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll fast_pow(ll b,ll p)
{
ll a=1;
while(p){
if(p&1)
a=a*b;
b=b*b;
p>>=1;
}
return a;
}
int main()
{
ll ncase;
cin>>ncase;
while(ncase--){
ll n;
cin>>n;
cout<<fast_pow(3,n)<<endl;
}
return 0;
}
分析:
盘子有三种摆法,但是必须按照从大到小开始放圆盘,还是3^n中吧。