189 8069 5689

汉诺塔问题的递归实现(扩展)-创新互联

Title:汉诺塔问题的递归实现

创新互联-专业网站定制、快速模板网站建设、高性价比汕尾网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式汕尾网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖汕尾地区。费用合理售后完善,十多年实体公司更值得信赖。

汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

汉诺塔问题的递归实现(扩展)

一、汉诺塔(基本)

汉诺塔问题是典型的分治算法问题,首先我们来讨论最基本的汉诺塔问题。假设有n个圆盘,三根柱子,a,b,c,需要把n个盘子(从下往上按照大小顺序摞着)从a柱移动到c柱,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

1.1 打印路径

采用分治法,分而治之,把大问题化解成小问题。故可以把n个盘子看成n-1个盘子,和第n个盘子,首先我们需要把n-1个盘子移动到b柱子上,然后把第n个盘子移动到c柱子上,最后把n-1个盘子移动到c柱子上,这样就用最少的移动次数完成了任务。

c++实现:

#include 
using namespace std;
void move(int n, char a, char b){
    cout<"< 0) {
        hanoi(n - 1, a, c, b);// 把n-1个盘子移动到c柱子上
        move(n, a, b);  // 把a移动到b
        hanoi(n - 1, c, b, a);  // 把第n-1个盘子从c柱子移动到b柱子上
    }
}
int main()
{
    int n;
   while(cin>>n){
        char a='a',b='b',c='c';
        hanoi(n,a,c,b);   //把n个盘子从a柱子移动到c柱子
    }
    return 0;
}

以上程序显示全部步骤的移动方法。

1.2 计算移动次数

如果要计算一共移动了多少次,找出规律即可。

假设移动n个盘子需要移动f(n)次,所以把n-1个盘子移动到b柱子上,需要移动f(n-1)次,然后把第n个盘子移动到c柱子上,需要移动1次,最后把n-1个盘子移动到c柱子上,需要移动f(n-1)次,综上所述,一共移动了

f(n) = 2 f(n-1) + 1

而:

 f(1) = 1;

 f(2) = 2*1+ 1;

 f(3) = 2(2*1+ 1)+ 1;

 .......

 f(n) = 2^n -1

C++实现:

//基本汉诺塔移动次数,可以算小于64的所有盘子数
#include 
using namespace std;
int main()
{
    int n;
   while(cin>>n){
        if(n>63||n<1) {
            cout<<"ERROR"<

二、汉诺塔(必须通过中间的柱子)

假设有n个圆盘,三根柱子,a,b,c,需要把n个盘子(上从下往上按照大小顺序摞着)从a柱移动到c柱,不允许直接从最左(右)边移到最右(左)边(每次移动一定是移到中间杆或从中间移出),也不允许大盘放到下盘的上面。

2.1 打印路径

参考acm题目:http://acm.hdu.edu.cn/showproblem.php?pid=2064

这道题和之前不同,约束了必须通过中间的柱子才可以,这也难不倒我们,我们只需要在递归的时候改变一下策略就可以完成任务。

解决方法:

依然采用分治法,分而治之,把大问题化解成小问题。最开始在a柱子上有n个盘子,我们可以把n个盘子看成n-1个盘子,和第n个盘子。首先我们需要把n-1个盘子移动到c柱子上(n-1个盘子只能由a移动到c或者由c移动到a,否则就不是最少次数了),然后把第n个盘子移动到b柱子上(不能直接由a移动到c),最后把n-1个盘子移动到c柱子上,这样就用最少的移动次数完成了任务。

c++实现:

#include 
using namespace std;
void move(int n, char a, char b){
    cout<"< 0) {
        hanoi(n - 1, a, c, b);
        move(n, a, b);
        hanoi(n - 1, c, a, b);
        move(n, b, c);
        hanoi(n - 1, a, c, b);
    }
}
int main()
{
    char a='a',b='b',c='c';
    hanoi(3,a,c,b);
    return 0;
}

2.2 计算移动次数

如果要计算一共移动了多少次,找出规律即可。

假设移动n个盘子需要移动f(n)次,所以把n-1个盘子移动到c柱子上,需要移动f(n-1)次,然后把第n个盘子移动到b柱子上,需要移动1次,然后把n-1个盘子移动到a柱子上,需要移动f(n-1)次,第n个盘子移动到c柱子上,需要移动1次,最后把n-1个盘子移动到c柱子上,需要移动f(n-1)次,综上所述,一共移动了

f(n) = 3 f(n-1) + 2

而:

 f(1) = 2;

 f(2) = 3*2+ 2;

 f(3) = 3(3*2+ 2)+ 2;

 .......

 f(n) = 3^n -1

C++实现:

#include 
using namespace std;
int main()
{
    int n;
    __int64 f[36];
    f[1]=2;
    for(int i=2;i<=35;i++) f[i]=3*f[i-1]+2;
   while(cin>>n){
        if(n>56||n<1){
            cout<<"ERROR!"<

三、汉诺塔(必须通过中间的柱子,允许大的放在最上面)

假设有n个圆盘,三根柱子,a,b,c,需要把n个盘子(上从下往上按照大小顺序摞着)从a柱移动到c柱,不允许直接从最左(右)边移到最右(左)边(每次移动一定是移到中间杆或从中间移出),也不允许大盘放到小盘的上面。如果我们允许大的盘子放到最上面会怎么样呢?(只允许大的放在最上面)当然最后需要的结果是盘子从小到大排在最右边。

3.1 打印路径

参考acm题目:http://acm.hdu.edu.cn/showproblem.php?pid=2077

这道题和之前不同,约束了必须通过中间的柱子才可以,也放宽了对大盘子的限制,所以肯定比第二种汉诺塔移动次数少,我们也是只需要在递归的时候改变一下策略就可以完成任务。

解决方法:

依然采用分治法,分而治之,把大问题化解成小问题。最开始在a柱子上有n个盘子,我们可以把n个盘子看成n-2个盘子,和第n-1,以及第n个盘子。首先我们需要把n-2个盘子移动到c柱子上(利用第二种类型的算法),然后把第n-1和n个盘子依次移动到b柱子上,再把n-2个盘子移动到a柱子上,再把第n和第n-1个盘子移动到柱子c上,这样就用最少的移动次数完成了任务。

C++实现:

#include 
using namespace std;
void move(int n, char a, char b){
    cout<"< 0) {
        hanoi(n - 1, a, c, b);
        move(n, a, b);
        hanoi(n - 1, c, a, b);
        move(n, b, c);
        hanoi(n - 1, a, c, b);
    }
}
void Hanoi_(int n, char a, char c, char b){
    if(n > 0) {
        hanoi(n - 2, a, c, b);
        if(n > 1)
        move(n - 1, a, b);
        move(n, a, b);
        hanoi(n - 2, c, a, b);
        move(n, b, c);
        if(n > 1)
        move(n - 1, b, c);
        hanoi(n - 2, a, c, b);
    }
}
int main()
{
    int n;
   while(cin>>n) {
        char a='a',b='b',c='c';
        Hanoi_(n,a,c,b);
    }
    return 0;
}

3.2 计算移动次数

如果要计算一共移动了多少次,找出规律即可。

假设移动n个盘子需要移动F(n)次,用第二种算法把n-2个盘子移动到c柱子上(利用第二种类型的算法),需要f(n-2)次,然后把第n-1和n个盘子依次移动到b柱子上,需要2次,再把n-2个盘子移动到c柱子上,再把第n和第n-1个盘子移动到柱子c上,需要2次,综上所述,一共移动了

f(n) = 3 f(n-2) + 4

而:

 f(n-2) = 3^(n-2)-1

故,f(n)=3^(n-1)+1

C++实现;

#include 
using namespace std;
int main()
{
    int T;
    cin>>T;
   while(T--){
        int n;
        cin>>n;
        if(n>51||n<1) {
            cout<<"ERROR"<

四、汉诺塔(加一根柱子)

假设有n个圆盘,三根柱子,a,b,c,需要把n个盘子(上从下往上按照大小顺序摞着)从a柱移动到c柱,再找来了一根一模一样的柱子d,通过这个柱子来更快的把所有的盘子移到第三个柱子上。

4.1 计算移动次数

参考acm题目:http://acm.hdu.edu.cn/showproblem.php?pid=1207

这道题和之前都有很大的不同,加了一根柱子,意味着有的时候可用3根柱子,有的时候可用4根柱子,当把j个小盘子移动到d盘上时,有四根柱子可用,而当把n-j个盘子从a移动到c时,仅有三根柱子可用。这里我们就要找到j的值,使所有移动的次数和最小。

解决方法:

依然采用分治法。首先把j个盘子移动到d柱子上(通过四个柱子可用的算法),需要B[j]次移动,然后把n-j个盘子移动到c柱子上(通过三个柱子可用的算法),需要A[n-j]次移动,,然后把d中的j个盘子移动到c柱子上,需要B[j]次移动。我们可以用j的大小循环,找到移动次数最小的j。

首先我们先计算移动的次数,核心公式为下面代码中的:flag = 2*B[ j ] + A[ i - j];  //j个盘子移动到第四个柱子,然后把剩下的i-j个在第四个不能用的情况下移到第三个

计算次数的c++实现:

#include 
using namespace std;
int main()
{
    int n;
    double a[65],b[65];  //数组a代表没加第四个柱子的结果,数组b代表加了第四个柱子的结果
    a[1]=1;
    for(int i=2;i<=64;i++) a[i]=2*a[i-1]+1;
    b[1]=1;
    b[2]=3;
    for(int i=3;i<=64;i++){
        double min=a[i],flag;
        for(int j=1;jflag) min=flag;
        }
        b[i]=min;
    }
   while(cin>>n){
        if(n>64||n<1){
            cout<<"ERROR!"<

4.2 打印路径

利用上面的算法,我们可以很容易得出路径

模拟移动步骤的C++实现:

#include 
using namespace std;
void move(int n, char a, char b){
    cout<"< 0) {
        hanoi_basic_3(n - 1, a, c, b);// 把n-1个盘子移动到b柱子上
        move(n, a, b);  // 把a移动到b
        hanoi_basic_3(n - 1, c, b, a);  // 把第n个盘子移动到b柱子上
    }
}
void hanoi(int n, char a, char c, char b, char d, int C[]){
    int j=C[n]; //j个盘子使用四个柱子的移动方式
    if(n > 0) {
        hanoi(j, a, d, b, c, C);// 把j个盘子移动到d柱子上
        hanoi_basic_3(n - j, a, c, b);// 把n-j个盘子移动到c柱子上(使用三个柱子的移动方式)
        hanoi(j, d, c, a, b, C);    // 把j个盘子移动到c柱子上
    }
}
int main()
{
    int n,flag_j;
    double A[65];  //未加第四个柱子时候的移动次数情况
    A[1]=1;
    for(int i=2;i<65;i++) A[i]=2*A[i-1]+1;
    double B[65],flag,min;
    int C[65]; //把前 c[j]个元素用四个柱子的方法,后i-j 个元素用三个柱子的方法
    C[1]=0;
    C[2]=0;
    for(int i=3;i<=64;i++){
        min=A[i];  //数组A代表没加第四个柱子的结果次数,数组b代表加了第四个柱子的结果次数
        B[1]=1;
        B[2]=3;
        flag_j=0;
        for(int j=1;jflag) {
                min=flag;
                flag_j=j;
            }
        B[i]=min;
        C[i]=flag_j;
        }
    }
   while(cin>>n){
        char a='a',b='b',c='c',d='d';
        hanoi(n,a,c,b,d,C);   //把n个盘子从a柱子移动到c柱子
    }
    return 0;
}

@码农同学,版权所有。

另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


网站名称:汉诺塔问题的递归实现(扩展)-创新互联
URL分享:http://cdxtjz.com/article/pidgd.html

其他资讯