首页 > 试题广场 >

用栈来求解汉诺塔问题

[编程题]用栈来求解汉诺塔问题
  • 热度指数:2313 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
汉诺塔问题比较经典,这里修改一下游戏规则:现在限制不能从最左侧的塔直接移动到最右侧,也不能从最右侧直接移动到最左侧,而是必须经过中间。求当塔有n层的时候,打印最优移动过程和最优移动总步数。

输入描述:
输入一个数n,表示塔层数


输出描述:
按样例格式输出最优移动过程和最优移动总步数
示例1

输入

2

输出

Move 1 from left to mid
Move 1 from mid to right
Move 2 from left to mid
Move 1 from right to mid
Move 1 from mid to left
Move 2 from mid to right
Move 1 from left to mid
Move 1 from mid to right
It will move 8 steps.

说明

当塔数为两层时,最上层的塔记为1,最下层的塔记为2

备注:
#include <bits/stdc++.h>
using namespace std;

int cnt = 0;

void F(int n, string a, string b, string c){
    if(n==1){
        printf("Move %d from %s to %s\n", n, a.c_str(), b.c_str());
        printf("Move %d from %s to %s\n", n, b.c_str(), c.c_str());
    }else{
        F(n-1, a, b, c);
        printf("Move %d from %s to %s\n", n, a.c_str(), b.c_str());
        F(n-1, c, b, a);
        printf("Move %d from %s to %s\n", n, b.c_str(), c.c_str());
        F(n-1, a, b, c);
    }
    cnt += 2;
}

int main(){
    int n;
    scanf("%d", &n);
    F(n, "left", "mid", "right");
    printf("It will move %d steps.\n", cnt);
    return 0;
}

发表于 2020-06-09 01:10:52 回复(0)
import java.util.*;

public class Main {
	static int steps = 0;
	
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		int n = scanner.nextInt();
        HanNuo(n, "left", "mid", "right");
        System.out.println("It will move "+steps+" steps.");
	}
	
	public static void HanNuo(int n,String a,String b,String c) {
		if(n==1) {
			System.out.println("Move "+n+" from "+a+" to "+b);
			System.out.println("Move "+n+" from "+b+" to "+c);
		}else {
			HanNuo(n-1,a,b,c);
			System.out.println("Move "+n+" from "+a+" to "+b);
			HanNuo(n-1,c,b,a);
			System.out.println("Move "+n+" from "+b+" to "+c);
			HanNuo(n-1,a,b,c);
		}
		steps+=2;
	}
}

发表于 2019-10-25 16:25:50 回复(2)
《程序员代码面试》Python题解
递归代码
# 递归求解
def hanoiProblem(num, left, mid, right):
    # num是需要移动的盘子数
    # left,mid,right是三座塔
    # 如果盘子数小于等于0
    if num < 1:
        # 移动步数是0
        return 0
    # 其它情况调用递归函数进行求解
    return process(num, left, mid, right, left, right)


# 汉诺塔递归求解函数
def process(num, left, mid, right, from_which, to):
    """
    :param num: 移动的盘子数
    :param left: 左塔
    :param mid: 中塔
    :param right: 右塔
    :param from_which: 盘子现在在哪座塔上
    :param to: 要移动到哪里
    :return: 最少移动步数
    """
    # 递归终止条件
    # 只剩一个盘子
    if num == 1:
        # 第一种情况
        # 从中塔到两端
        # 或者从两端到中塔
        if from_which == 'mid'&nbs***bsp;to == 'mid':
            print("Move 1 from {} to {}".format(from_which, to))
            return 1
        # 第二种情况
        # 从左塔到右塔
        # 或者从右塔到左塔
        else:
            print("Move 1 from {} to {}".format(from_which, mid))
            print("Move 1 from {} to {}".format(mid, to))
            return 2
    # 递归体
    # 第一种情况
    # 从中塔到两端
    # 或者从两端到中塔
    if from_which == 'mid'&nbs***bsp;to == 'mid':
        # 需要三步操作
        # 先将num-1个盘子从from_which移动到除了中塔的另外一座塔上
        another = right if (from_which == 'left'&nbs***bsp;to == 'left') else left
        part1 = process(num - 1, left, mid, right, from_which, another)
        # 将第num个盘子从from_which移动到to
        print("Move {} from {} to {}".format(num, from_which, to))
        part2 = 1
        # 将num-1个盘子从另外一座塔移动到to
        part3 = process(num - 1, left, mid, right, another, to)
        # 最少移动步数
        return part1 + part2 + part3
    # 第二种情况
    # 从左塔移动到右塔
    # 或者从右塔移动到中塔
    else:
        # 分为5步
        # 先将num-1个盘子从from_which移动到to
        part1 = process(num - 1, left, mid, right, from_which, to)
        # 将第num个盘子从from_which移动到中塔
        print("Move {} from {} to {}".format(num, from_which, mid))
        part2 = 1
        # 将num-1个盘子从to移动到from_which
        part3 = process(num - 1, left, mid, right, to, from_which)
        # 将第num个盘子从中塔移动到to
        print("Move {} from {} to {}".format(num, mid, to))
        part4 = 1
        # 最后将num-1个盘子从from_which移动到to
        part5 = process(num - 1, left, mid, right, from_which, to)
        # 最少移动步数
        return part1 + part2 + part3 + part4 + part5


# 移动的盘子数
n = int(input())
step = hanoiProblem(n, 'left', 'mid', 'right')
print("It will move {} steps.".format(step))

用栈实现的代码
# 定义操作列表
# 分别是:
# 不做任何操作
# 左塔到中塔
# 中塔到左塔
# 中塔到右塔
# 右塔到中塔
action = ['No', 'LToM', 'MToL', 'MToR', 'RToM']


# 用栈求解汉诺塔问题
def hanoiProblem(num, left, mid, right):
    """
    :param num:移动的盘子数
    :param left:左塔
    :param mid:中塔
    :param right:右塔
    :return:最少移动步数
    """
    # 用列表模拟栈
    # 左塔
    ls = []
    # 中塔
    ms = []
    # 右塔
    rs = []
    # 将最大值加入栈中
    # 方便后面处理
    max_value = 10 ** 10
    ls.append(max_value)
    ms.append(max_value)
    rs.append(max_value)
    # 向右塔中加入num个盘子
    for i in range(num, 0, -1):
        ls.append(i)
    # 初始化操作序列记录
    record = [action[0]]
    # 初始化最少移动步数
    step = 0
    # 当num座塔没有完全被移动到右塔上时
    while len(rs) != num + 1:
        # 分别进行四种操作的试探
        step += fstackTotstack(record, action[2], action[1], ls, ms, left, mid)
        step += fstackTotstack(record, action[1], action[2], ms, ls, mid, left)
        step += fstackTotstack(record, action[4], action[3], ms, rs, mid, right)
        step += fstackTotstack(record, action[3], action[4], rs, ms, right, mid)
    return step


# 出栈、入栈操作
def fstackTotstack(record, pre_action, now_action, fstack, tstack, from_which, to):
    """
    :param record: 操作序列记录
    :param pre_action: 上个操作
    :param now_action:当前的操作
    :param fstack:出栈
    :param tstack:入栈
    :param from_which:出塔
    :param to:入塔
    :return:移动次数
    """
    # 如果满足操作条件
    if record[0] != pre_action and fstack[-1] < tstack[-1]:
        # 入栈
        tstack.append(fstack.pop())
        print("Move {} from {} to {}".format(tstack[-1], from_which, to))
        # 更新记录
        record[0] = now_action
        return 1
    return 0


n = int(input())
step = hanoiProblem(n, 'left', 'mid', 'right')
print("It will move {} steps.".format(step))


发表于 2021-12-26 08:08:22 回复(1)
首先我们考虑一下传统汉诺塔游戏的思路,可以抽象成如下的三个步骤:
  1. 1~n-1left柱借助right柱移动到mid柱;
  2. 然后将nleft柱直接移动到right柱;
  3. 最后再将1~n-1mid柱借助left柱移动到right柱。
重点观察第二步,因为只有它是一步具体的操作,只移动一块圆盘,而其他两步都是个大的递归,嵌套了其他操作并不好分析。此时由于不能直接从left柱移动到right柱,因此必须先将其移动到mid柱。那要将其先移动到mid柱,mid柱上就不能有比n圆盘小的圆盘,所以在进行第一步的时候,就不能将1~n-1号圆盘移动到mid柱上了。既然如此,1~n-1号圆盘就只能从left柱借助mid柱先移动到right柱上,此时n号圆盘就能从left柱先移动到mid柱上。可我们第二步的目的是要将n号圆盘从left柱移动到right柱上,而right柱上现在有1~n-1号圆盘,因此需要加一步“将right柱上的1~n-1号圆盘从right柱借助mid柱(此时mid柱上只有个n圆盘,它可以作为1~n-1号圆盘的跳板)移动到left柱”的操作,之后再将n号圆盘从mid柱移动到right柱。至此,完成了前两步的壮举,最后只需要将现在left柱上的1-n-1号圆盘借助mid柱移动到right柱的n号圆盘上就可以了。
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Main {
    private static int stages = 0;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        hanoi(n, "left", "mid", "right");
        System.out.println("It will move "+ stages +" steps.");
    }
    
    private static void hanoi(int n, String left, String mid, String right) {
        if(n == 1){
            // 剩一个,借助mid柱把n从left移动到right
            move(n, left, mid);
            move(n, mid, right);
        }else{
            // 第一步,把1~n-1从left柱借助mid柱移动到right柱
            hanoi(n - 1, left, mid, right);
            // 第二步,把n从left柱借助mid柱移动到right柱
            move(n, left, mid);                // 先将n从left移动到mid
            hanoi(n - 1, right, mid, left);    // 再将mid上面的1~n-1移动借助mid到left上
            move(n, mid, right);               // 最后将n从mid移动到right上
            // 第三步,将1~n-1从left柱借助mid柱移动到right柱
            hanoi(n - 1, left, mid, right);
        }
    }
    
    // 将n从from移动到to
    private static void move(int n, String from, String to) {
        System.out.println("Move " + n + " from " + from + " to " + to);
        stages ++;
    }
}

编辑于 2021-12-08 15:16:18 回复(0)
汉诺塔问题是帮助理解递归的经典,每次看这道题都会有不同的理解。和增加限制条件前相比,这道题的关键是理解:在新的限制条件下,递归条件处理发生了什么变化。

粗略的分,每一次移动只有两种情况:两端互相移动(左->右,右->左),或者中间柱作为src或dst,那么递归基就应该根据这两种情况区分对待。除此之外,每次递归的有效操作实际上只是移动了src柱上的最后一块,上面n-1块的移动是一种假想的虚移动,此时也需要个根据上述情况区分对待。

明白这两点,直接上代码,很好阅读:
#include <bits/stdc++.h>

using namespace std;

void hanoid(int n, string src, string mid, string dst, int &count){
    count ++;
    if (n==1){
        if (src=="mid" || dst=="mid")a
            cout << "Move " << n << " from " << src << " to " << dst <<endl;
        else{
            count ++;
            cout << "Move " << n << " from " << src << " to " << mid <<endl;
            cout << "Move " << n << " from " << mid << " to " << dst <<endl;
        }
        return;
    }
    // 退化为第一种情况
    if(src=="mid" || dst=="mid"){
        hanoid(n-1, src, dst, mid, count);
        cout << "Move " << n << " from " << src << " to " << dst <<endl;
        hanoid(n-1, mid, src, dst, count);
    }
    
    else{
        count ++;
        hanoid(n-1, src, mid, dst, count);
        cout << "Move " << n << " from " << src << " to " << mid <<endl;
        hanoid(n-1, dst, mid, src, count);
        cout << "Move " << n << " from " << mid << " to " << dst <<endl;
        hanoid(n-1, src, mid, dst, count);
    }
}


int main(){
    int n, count=0;
    cin >> n;
    string src="left", mid="mid", right="right";
    hanoid(n, src, mid, right, count);
    cout << "It will move " << count << " steps.";
    return 0;
}

发表于 2020-02-11 09:13:15 回复(0)
尝试着解释一下,先来看看一般的汉诺塔问题,A,B,C三个柱子,将A柱子上的圆盘移动到C上面,但是要保持小盘必须落在大盘上面才可以直接移动,并且一次只能移动一个圆盘。通过递归求解,如果n-1个盘子已经移动到B,那么n直接移动到C,然后将n-1从B移动到C即可。
1、将n-1个圆盘从A移动到B,借助C    hanota(n-1, A, C, B)
2、将n从A移动到C                               move(n, A, C)
3、将n-1圆盘从B移动都C,借助A       hanota(n-1, B, A, C)
那么代码就是
import java.util.Scanner;
public class Hanota {

    public static void move(int n, String from, String to){
        System.out.println("将"+ n + "从" + from + "移动到" + to);
    }

    public static void hanota(int n, String A, String B, String C){
        if(n == 1){
            move(n, A, C);
        }else{
            hanota(n-1, A, C, B);
            move(n, A, C);
            hanota(n-1, B, A, C);
        }
    }


    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        hanota(n, "A", "B", "C");
    }
}
现在多了限制,就是移动必须经过中间的B,不能直接从A到C或者从C到A,那步骤修改一下:
1、将n-1个圆盘从A移动到C,借助B hanota(n-1, A, B, C)
2、将n从A移动到B                          move(n-1, A, B)
3、将n-1个圆盘从C移动到A,借助B hanota(n-1, C,B,A)
4、将n从B移动到C                          move(n, B, C)
5、将n-1个圆盘从A移动到C,借助B hanota(n-1, A, B, C)
那么代码就是
import java.util.Scanner;
public class Main {
    static private int count=0;

    public static void move(int n, String from, String to){
        System.out.println("Move " + n + " from " + from + " to " + to);
        count++;
    }


    public static void hanota(int n, String A, String B, String C){
        if(n == 1){
            move(n, A, B);
            move(n, B, C);
            return;
        }else{
            hanota(n-1, A, B, C);
            move(n, A, B);
            hanota(n-1, C, B, A);
            move(n, B, C);
            hanota(n-1, A, B, C);
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        hanota(n, "left", "mid", "right");
        System.out.println("It will move " + count + " steps.");
    }
}
参考
汤姆凯特。的代码








发表于 2020-02-25 17:58:25 回复(1)
//c++ 用栈来求解汉诺塔问题
#include <iostream>
#include<stack>
using namespace std;
int f(int& recode, int pre, int now, stack<int>* fs, stack<int>* ts,
    string from, string to) {
    if (fs->empty()) return 0;
    if (recode != pre && (ts->empty() || fs->top() < ts->top())) {
        int a = fs->top();
        fs->pop();
        ts->push(a);
        cout << "Move " << a << ' ' << "from "
            << from << ' ' << "to " << to << '\n';
        recode = now;
        return 1;
    }
    return 0;
}

int main() {
    int n;
    cin >> n;
    stack<int>* ls = new stack<int>();
    stack<int>* ms = new stack<int>();
    stack<int>* rs = new stack<int>();

    for (int i = n; i > 0; i--) {
        ls->push(i);
    }

    int step = 0;
    int recode = 0;
    while (rs->size() != n) {
        step += f(recode, 21, 12, ls, ms, "left", "mid");
        step += f(recode, 12, 21, ms, ls, "mid", "left");
        step += f(recode, 32, 23, ms, rs, "mid", "right");
        step += f(recode, 23, 32, rs, ms, "right", "mid");
    }

    printf("It will move %d steps.", step);
}
// 64 位输出请用 printf(\"%lld\")
编辑于 2024-03-21 22:11:26 回复(0)
有两种方法,一是递归一是迭代。
方法1:递归
移动n个元素从1到3,包括以下步骤:
移动n-1个元素从1到3;(递归)
移动n从1到2;
移动n-1个元素从3到1;(递归)
移动n从2到3;
移动n-1个元素从1到3;(递归)
代码如下:
#include "iostream"
#include "vector"
#include "string"

using namespace std;

vector<string> ump;
int count;

void dfs(int n,int src,int dest)
{
    if(n==1) {
        cout<<"Move 1 from "<<ump[src-1]<<" to mid"<<endl;
        cout<<"Move 1 from mid to "<<ump[dest-1]<<endl;
        count+=2;
        return;
    }
    dfs(n-1,src,dest);
    cout<<"Move "<<n<<" from "<<ump[src-1]<<" to mid"<<endl;
    count++;
    dfs(n-1,dest,src);
    cout<<"Move "<<n<<" from mid to "<<ump[dest-1]<<endl;
    count++;
    dfs(n-1,src,dest);
}

int main(){
    int n;
    cin>>n;
    count=0;
    ump={"left","mid","right"};
    dfs(n,1,3);
    cout<<"It will move "<<count<<" steps.";
    return 0;
}
方法2:迭代
移动过程由4候选操作:从1到2,从2到1,从2到3,从3到2;
要遵循两个原则:一是只能小压大,二是不能是上一步的逆操作;
这样,第一步是移动1从1到2,后面每一步在两个原则下只能有一个操作满足要求。

发表于 2022-05-12 12:24:16 回复(0)
fkl头像 fkl
#include <bits/stdc++.h>

using namespace std;

enum action {
    No, L2M, M2L, R2M, M2R
};


int fs2s(enum action record[], enum action prenoact, enum action nowact,
         stack<int>& fs, stack<int>& ts, string from, string to) {

    if (record[0] != prenoact && fs.top() < ts.top()) {
        ts.push(fs.top());
        fs.pop();
        cout << "Move " << ts.top() << " from " << from << " to " << to << endl;
        record[0]  = nowact;
        return 1;
    }

    return 0;
}

int hano(int num, string left, string mid, string right) {

    stack<int> ls;
    stack<int> ms;
    stack<int> rs;

    ls.push(INT_MAX);
    ms.push(INT_MAX);
    rs.push(INT_MAX);

    for (int i = num; i > 0; i--) {
        ls.push(i);
    }

    action record[] = {No};
    int step = 0;
    while (rs.size() != num + 1) {
        step += fs2s(record, M2L, L2M, ls, ms, left, mid);
        step += fs2s(record, L2M, M2L, ms, ls, mid, left);
        step += fs2s(record, R2M, M2R, ms, rs, mid, right);
        step += fs2s(record, M2R, R2M, rs, ms, right, mid);
    }
    return step;
}


int main() {
    int n;
    cin >> n;
    int count = hano(n, "left", "mid", "right");
    cout << "It will move " << count << " steps.";

}

发表于 2022-04-22 02:22:38 回复(0)
贴一个栈实现的c++版本
#include <iostream>
#include <limits.h>
#include <stack>

using namespace std;

enum Action {No = 0, LToM, MToL, MToR, RToM};

int fStackTotStack(Action& record, Action preAct, Action curAct, stack<int>& fStack, stack<int>& tStack, string from, string to) 
{
//     cout << "record = " << record << " preAct = " << preAct << " curAct = " << curAct << ", from = " << from << ", to = " << to << endl;
    if (record != preAct && fStack.top() < tStack.top()) {
        tStack.push(fStack.top()); fStack.pop();
        cout << "Move " << tStack.top() << " from " << from << " to " << to << endl;
        record = curAct;
        return 1;
    }
    
    return 0;
}

int help(int n, string left, string mid, string right) {
    stack<int> ls, ms, rs;
    ls.push(INT_MAX);
    ms.push(INT_MAX);
    rs.push(INT_MAX);
    
    for (int i = n; i > 0; i--) {
        ls.push(i);
    }
    
    Action record = No;
    int step = 0;
    while (rs.size() != n + 1) {
        step += fStackTotStack(record, MToL, LToM, ls, ms, left, mid);
        step += fStackTotStack(record, LToM, MToL, ms, ls, mid, left);
        step += fStackTotStack(record, RToM, MToR, ms, rs, mid, right);
        step += fStackTotStack(record, MToR, RToM, rs, ms, right, mid);
    }
    
    return step;
}



int main()
{
    int n;
    cin >> n;
    int cnt = help(n, "left", "mid", "right");
    
    cout << "It will move " << cnt << " steps.";
}

发表于 2022-04-12 11:52:14 回复(0)
参考评论区大佬,真正搞懂汉诺塔问题
# include <bits/stdc++.h>

using namespace std;

// count 得传引用
void move(int n, string from, string to, int& count){
    cout << "Move " << n << " from " << from << " to " << to << endl;
    count++;
}

void hanota(int n, string left, string mid, string right, int& count){
    if(n == 1){
        move(n, left, mid, count);
        move(n, mid, right, count);
        return;
    }
    else {
        // 将n-1个圆盘从A移动到C,借助B hanota(n-1, A, B, C) 
        hanota(n - 1, left, mid, right, count);
        // 将n从A移动到B
        move(n, left, mid, count);
        // 将n-1个圆盘从C移动到A,借助B hanota(n-1, C,B,A) 
        hanota(n - 1, right, mid, left, count);
        // 将n从B移动到C     
        move(n, mid, right, count);
        // 将n-1个圆盘从A移动到C,借助B hanota(n-1, A, B, C) 
        hanota(n - 1, left, mid, right, count);
    }
}

int main()
{
    int n;
    cin >> n;
    int count = 0;
    hanota(n, "left", "mid", "right", count);
    cout << "It will move " << count << " steps.";
}


发表于 2022-04-07 22:37:30 回复(0)
#用python3写
import  sys
class HanoiProblem():
    def __init__(self):
        self.lS=[]
        self.mS=[]
        self.rS=[]
        self.lS.append(100)
        self.mS.append(100)
        self.rS.append(100)
        self.No='No'
        self.LToM='LToM'
        self.MToL='MToL'
        self.MToR='MToR'
        self.RToM='RToM'
    def fStackTotstack(self,record, preNoAct, nowAct, fStack,tStack,From,To):
        if ((record[0]!=preNoAct) and (fStack[-1] < tStack[-1])):
            tStack.append(fStack.pop())
            print("Move "+ str(tStack[-1])+ " from "+From+" to "+To)
            record[0]=nowAct
            return 1
        return 0

    def start(self,num,left,mid,right):
        for i in range(num,0,-1):
            self.lS.append(i)
        record=[self.No]
        step=0
        while (len(self.rS)!=num+1):
            step += self.fStackTotstack(record, self.MToL,self.LToM, self.lS, self.mS, left, mid)
            step += self.fStackTotstack(record, self.LToM,self.MToL, self.mS, self.lS, mid, left)
            step += self.fStackTotstack(record, self.RToM,self.MToR, self.mS, self.rS, mid, right)
            step += self.fStackTotstack(record, self.MToR,self.RToM, self.rS, self.mS, right, mid)
        return  step

num=int(input())
HP=HanoiProblem()
result=HP.start(num,'left','mid','right')
print('It will move {} steps.'.format(result))
发表于 2021-08-16 14:09:50 回复(0)

今天吃屁->又是优秀的cv程序员

let n=parseInt(readline()) 
var count=0
const move=function(num,from,to){
    count++
    console.log("Move "+num+' from'+from+"to"+to)
}
const hanoi=function(num,from,to,aux){
    if(num==1){
            move(num,from,aux)
            move(num,aux,to)
    }else{
        hanoi(num-1,from,to,aux)
        move(num,from,aux)
        hanoi(num-1,to,from,aux)
        move(num,aux,to)  
        hanoi(num-1,from,to,aux)
    }
}
hanoi(n," left "," right "," mid ")
console.log("It will move "+count+" steps.")
发表于 2021-06-25 14:22:36 回复(0)
#include <iostream>
using namespace std;
//n表示当前还有多少层
int myMove(string from, string to, int n)
{
    if(n == 1){
        if(from == "mid" || to == "mid"){
            cout << "Move " << 1 << " from " << from << " to " << to << endl;
            return 1;
        }else{
            cout << "Move " << 1 << " from " << from << " to mid" << endl;
            cout << "Move " << 1 << " from mid to " << to << endl; 
            return 2;
        }
    }
    if(from == "mid" || to == "mid"){
        string another = (from == "left" || to == "left") ? "right" : "left";
        int c1 = myMove(from, another, n - 1);
        cout << "Move " << n << " from " << from << " to " << to << endl;
        int c2 = myMove(another, from, n - 1);
        return c1 + c2 + 1;
    }else{
        int c1 = myMove(from, to, n - 1);
        cout << "Move " << n << " from " << from << " to mid" << endl;
        int c2 = myMove(to, from, n - 1);
        cout << "Move " << n << " from mid to " << to << endl;
        int c3 = myMove(from, to, n - 1);
        return c1 + c2 + c3 + 2;
    }
}
int main(void){
    int n;
    cin >> n;
    int myCount = myMove("left", "right", n);
    cout << "It will move " << myCount << " steps." << endl;
}
编辑代码出现的问题:
内存不满或段错误: 原因-> 使用递归的时候忘了return
错误使用move 但凡函数统统添加myMove, myCount
发表于 2021-04-22 11:29:39 回复(0)
helper方法就是遍历方法。helper(n,true)表示将左侧的塔都移动到右侧。helper(n,false)表示将右侧的塔都移动到左侧(当然要满足必须借助中间塔的条件)。
以helper(n,true)为例:
  1. 首先需要将n-1个塔从左侧移动到右侧,交给函数helper(n-1,true)处理。
  2. 然后将第n个塔直接移动到中间,打印路径,统计步数。
  3. 将n-1个塔从右侧移动到左侧,交给helper(n-1,false)处理。
  4. 将第n个塔直接从中间移动到右侧,打印路径,统计步数。
  5. 将n-1个塔从左侧移动到右侧。交给函数helper(n-1, true)处理。
import java.io.*;
public class Main {
    static int count = 0;
    public static void main(String[] args) throws IOException {
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.valueOf(bf.readLine());
        bf.close();
        count = 0;
        System.out.print(helper(n, true));
        System.out.println("It will move "+count+" steps.");
    }

    public static StringBuilder helper(int n, boolean bool) {
        if (n == 0)
            return new StringBuilder();
        StringBuilder sb;
        sb = helper(n - 1, bool);
        if (bool) {
            sb.append("Move " + n + " from left to mid\n");
        } else {
            sb.append("Move " + n + " from right to mid\n");
        }
        sb.append(helper(n - 1, !bool));
        if (!bool) {
            sb.append("Move " + n + " from mid to left\n");
        }else{
            sb.append("Move " + n + " from mid to right\n");
        }         sb.append(helper(n - 1, bool));
        count += 2;          return sb;
    }
}
观察到使用了两次helper(n-1, bool),所以可以把该路径及步数记录下来。这样直接从400ms减少到100ms。
    public static StringBuilder helper(int n, boolean bool) {
        if (n == 0)
            return new StringBuilder();

        StringBuilder sb = new StringBuilder(), temp;
        int num = count;
        temp = helper(n - 1, bool);
        sb.append(temp);
        num = count - num;
        if (bool) {
            sb.append("Move " + n + " from left to mid\n");
        } else {
            sb.append("Move " + n + " from right to mid\n");
        }
        sb.append(helper(n - 1, !bool));
        if (!bool) {
            sb.append("Move " + n + " from mid to left\n");
        } else {
            sb.append("Move " + n + " from mid to right\n");
        }
        count += 2;
        sb.append(temp);
        count += num;
        
        return sb;
    }

发表于 2020-06-05 16:14:41 回复(0)
Golang版本 AC代码
这种问题可以将n-1看成是一个整体,可以尝试去理解一些小数目的移动,大数目的移动就不要去想了,会头铁
package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	str, _ := reader.ReadString('\n')
	str = strings.ReplaceAll(str, "\n", "")
	n, err := strconv.Atoi(str)
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println("It will move", hanoi(n, "left", "mid", "right"), "steps.")
}

func hanoi(n int, left, mid, right string) int {
	if n == 1 {
		fmt.Println("Move 1 from", left, "to", mid)
		fmt.Println("Move 1 from", mid, "to", right)
		return 2
	} else {
		res := hanoi(n-1, left, mid, right)
		fmt.Println("Move", n, "from", left, "to", mid)
		res += hanoi(n-1, right, mid, left)
		fmt.Println("Move", n, "from", mid, "to", right)
		res += hanoi(n-1, left, mid, right)
		return res + 2
	}
}



发表于 2019-09-19 21:51:10 回复(0)
#include<stdlib.h>
#include<iostream>
#include<vector>
using namespace std;

void print(int a,int src,int dest) {
    string str1,str2;
    if (src==0) str1="mid";
    else str1=(src==1)?"left":"right";
    if (dest==0) str2="mid";
    else str2=(dest==1)?"left":"right";
    if (abs(src-dest)==1) cout<<"Move "<<a<<" from "<<str1<<" to "<<str2<<endl;
    else {
        cout<<"Move "<<a<<" from "<<str1<<" to "<<"mid"<<endl;
        cout<<"Move "<<a<<" from "<<"mid"<<" to "<<str2<<endl;
    }
}

int moveta(int a,int b,int src,int dest) {
    if (a==b) {
        print(a,src,dest);
        return abs(src-dest)>1?2:1;
    }
    int res=0;
    if (abs(src-dest)>1) {
        res+=moveta(a,b,src,0);
        res+=moveta(a,b,0,dest);
    } else if (dest==0) {
        res+=moveta(a,b-1,src,0);
        res+=moveta(a,b-1,0,-src);
        res+=moveta(b,b,src,0);
        res+=moveta(a,b-1,-src,0);
    } else {
        res+=moveta(a,b-1,0,-dest);
        res+=moveta(b,b,0,dest);
        res+=moveta(a,b-1,-dest,0);
        res+=moveta(a,b-1,0,dest);
    }
    return res;
}

int main() {
    int n;
    cin>>n;
    int dest=0,res=moveta(1,n,1,-1);
    cout<<"It will move "<<res<<" steps."<<endl;
}

发表于 2019-09-09 21:34:52 回复(0)

书上给的代码分析的似乎有些复杂

import java.util.Scanner;

public class Main {
    int hanoi(int num){
        if (num < 1){
            return 0;
        }
        return process(num, "left", "right");
    }

    private int process(int num, String from, String to){
        if (num == 1){
            if (from.equals("mid") || to.equals("mid")){
                System.out.println("Move 1 from " + from + " to " + to);
                return 1;
            } else{
                System.out.println("Move 1 from " + from + " to mid");
                System.out.println("Move 1 from mid to " + to);
                return 2;
            }
        }else{
            int part1 = process(num - 1, from, to);
            System.out.println("Move " + num + " from " + from + " to mid");
            int part2 = 1;
            int part3 = process(num - 1, to, from);
            System.out.println("Move " + num + " from mid to " + to);
            int part4 = 1;
            int part5 = process(num - 1, from, to);
            return part1 + part2 + part3 + part4 + part5;
        }
    }


    public static void main(String[] args) {
        int layers = new Scanner(System.in).nextInt();
        int steps = new Main().hanoi(layers);
        System.out.println("It will move " + steps + " steps.");
    }
}
发表于 2019-09-09 11:13:34 回复(0)
应该是这样的,不知道为什么一直在报下面的异常。
public class Main {
    public void main(int n) {
        hanNuo(n,"left","mid","right");
        if(n==1){
            System.out.println("It will move 2 steps.");
        }else{
            int count= (int) (3*Math.pow(2,n-1)+2);
            System.out.println("It will move "+count+" steps.");
        }
    }
   
      public void hanNuo(int n,String left,String mid,String right){
        if(n==1){
            System.out.println("Move "+n+" from "+left+" to "+mid);
            System.out.println("Move "+n+" from "+mid+" to "+right);
        }else{
            hanNuo(n-1,left,mid,right);//将n-1个从left移到right上
            System.out.println("Move "+n+" from "+left+" to "+mid);//将第n个从left移到mid上
            hanNuo(n-1,right,mid,left);//将n-1个从right移到left上
            System.out.println("Move "+n+" from "+mid+" to "+right);
            hanNuo(n-1,left,mid,right);
        }
    }
}


发表于 2019-08-05 15:07:24 回复(0)

问题信息

上传者:小小
难度:
19条回答 3954浏览

热门推荐

通过挑战的用户

查看代码