算法-经典趣题-马踏棋盘(又称骑士周游)

本文为joshua317原创文章,转载请注明:转载自joshua317博客 算法-经典趣题-马踏棋盘(又称骑士周游) - joshua317的博客

一、问题

马踏棋盘问题,又称骑士漫步、,它是一个非常有趣的智力问题。马踏棋盘问题的大意如下:

国际象棋的棋盘有8行8列共64个单元格,无论将马放于棋盘的哪个单元格,都可让马踏遍棋盘的每个单元格。问马应该怎么走才可以踏遍棋盘的每个单元格?

二、分析

我们来分析一下马踏棋盘问题。在国际象棋中,马只能走“日”字形,但是马位于不同的位置其可以走的方向有所区别:

当马位于棋盘中间位置时,马可以向8个方向跳动;

当马位于棋盘的边或角时,马可以跳动的方向将少于8个。

另外,为了求解最少的走法,当马所跳向的8个方向中的某一个或几个方向已被马走过,那么马也将跳至下一步要走的位置。可以使用递归的思想来解决马踏棋盘问题。

我们可以使用递归的思想来解决马踏棋盘问题,其操作步骤如下:

(1)从起始点开始向下一个可走的位置走一步。

(2)接着以该位置为起始,再向下一个可走的位置走一步。

(3)这样不断递归调用,直到走完64格单元格,就找到一个行走方案。

这里需要注意的是,如果在行走过程中,某个位置向8个方向都没有可走的点,则需要退回上一步,从上一个位置的另外一个可走位置继续递归调用,直至找到一个行走方案。

三、编程

package com.joshua317;

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        int i, j;
        Coordinate start = new Coordinate(0, 0);
        System.out.println("马踏棋盘问题");
        System.out.println("请输入马的一个其实位置(x,y)");
        Scanner scanner = new Scanner(System.in);
        start.x = scanner.nextInt();
        start.y = scanner.nextInt();
        if (start.x < 1 || start.x > 8 || start.y < 1 || start.y > 8) {
            System.out.println("起始位置输入有误!END!!!");
        } else {
            for (i = 0; i < 8; i++) {
                for (j = 0; j < 8; j++) {
                    HorseChessBoard.chessBoard[i][j] = 0;
                }
            }

            start.x--;
            start.y--;
            HorseChessBoard.curstep = 1;
            HorseChessBoard.move(start);
        }
    }
}

class Coordinate {
    int x,y;
    public Coordinate (int a, int b) {
        x = a;
        y = b;
    }
}
class HorseChessBoard {
    static int[][] chessBoard = new int[8][8];
    static int curstep;
    //马可以走的8个方向
    static Coordinate[] direction = {
            new Coordinate(-2, 1),new Coordinate(-1, 2),new Coordinate(1, 2),new Coordinate(2, 1),
            new Coordinate(2, -1),new Coordinate(1, -2),new Coordinate(-1, -2),new Coordinate(-2, -1)
    };

    public static void move(Coordinate curpos)
    {
        Coordinate next = new Coordinate(0, 0);
        int i, j;
        //判断是否越界
        if (curpos.x < 0 || curpos.x > 7 || curpos.y < 0 || curpos.y > 7) {
            return;
        }
        //判断是否走过
        if (chessBoard[curpos.x][curpos.y] != 0) {
            return;
        }
        //保存步数
        chessBoard[curpos.x][curpos.y] = curstep;
        curstep++;
        //如果棋盘位置都走完
        if (curstep > 64) {
            for (i = 0; i < 8; i++) {
                for (j = 0; j < 8; j++) {
                    System.out.printf("%5d", chessBoard[i][j]);
                }
                System.out.println("");
            }
            System.exit(0);
        } else {
            //8个可能的方向
            for (i = 0; i < 8; i++) {
                next.x = curpos.x + direction[i].x;
                next.y = curpos.y + direction[i].y;
                if (next.x < 0 || next.x > 7 || next.y < 0 || next.y > 7) {

                } else {
                    move(next);
                }
            }
        }
        //清除步数序号
        chessBoard[curpos.x][curpos.y] = 0;
        curstep--;
    }
}

执行该程序,输入马的一个起始位置(1,1),得到的结果如下图所示。

如果输入马的另外一个起始位置(8,8),得到的结果如下图所示。

四、扩展

马踏棋盘是经典的程序设计问题之一,主要的解决方案有两种:

一种是基于深度优先搜索的方法,另一种是基于贪婪算法的方法。

第一种基于深度优先搜索的方法是比较常用的算法,深度优先搜索算法也是数据结构中的经典算法之一,主要是采用递归的思想,一级一级的寻找,最后找到合适的解。

而基于贪婪的算法则是依据贪婪算法的思想设置一种标准,然后依据标准进行选择,从而得到解,但是他不一定能够得到最优解。

深度优先搜索属于图算法的一种,英文缩写为DFS即Depth First Search.其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,但对范围相当广泛的许多问题他能产生整体最优解或者是整体最优解的近似解。

基于深度优先搜索的算法就是依据当前点找到下一个可能的点,然后对这个点进行深度优先搜索,然后依次递归,当出现条件不满足时,退回来,采用其他的路劲进行搜索,最后肯定能够得到对应的结果。

五、通过贪心算法实现

采用贪心算法,对路径有目的地筛选,尽量选择出口少的路先走,也就是对当前点的下一个落脚点(可能是8个)进行排序,优先走可走的路最少的那个点,使得走法较好。通俗来讲,就是先预判下一个可能落脚点的出口数,出口数最少的先走掉。


本文为joshua317原创文章,转载请注明:转载自joshua317博客 算法-经典趣题-马踏棋盘(又称骑士周游) - joshua317的博客

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 你好,以下是用 C 语言编写的马踏棋盘算法: #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_SIZE 8 int board[MAX_SIZE][MAX_SIZE]; int move_x[8] = {1, 2, 2, 1, -1, -2, -2, -1}; int move_y[8] = {2, 1, -1, -2, -2, -1, 1, 2}; void print_board() { for (int i = ; i < MAX_SIZE; i++) { for (int j = ; j < MAX_SIZE; j++) { printf("%2d ", board[i][j]); } printf("\n"); } } int is_valid_move(int x, int y) { if (x < || x >= MAX_SIZE || y < || y >= MAX_SIZE) { return ; } if (board[x][y] != ) { return ; } return 1; } int solve(int x, int y, int step) { board[x][y] = step; if (step == MAX_SIZE * MAX_SIZE) { return 1; } for (int i = ; i < 8; i++) { int next_x = x + move_x[i]; int next_y = y + move_y[i]; if (is_valid_move(next_x, next_y)) { if (solve(next_x, next_y, step + 1)) { return 1; } } } board[x][y] = ; return ; } int main() { memset(board, , sizeof(board)); if (solve(, , 1)) { printf("Solution found:\n"); print_board(); } else { printf("No solution found.\n"); } return ; } 希望能对你有所帮助。 ### 回答2: 马踏棋盘是一种著名的递归算法,主要用于解决如何使马在棋盘上每个格子只经过一次的问题。下面是使用C语言编写的一个完整的马踏棋盘算法: ```c #include <stdio.h> #define SIZE 8 // 棋盘大小 // 马在棋盘上的移动方向和下一个位置的坐标 int nextX[8] = {1, 2, 2, 1, -1, -2, -2, -1}; int nextY[8] = {2, 1, -1, -2, -2, -1, 1, 2}; // 棋盘 int chessboard[SIZE][SIZE]; // 检查下一个位置是否合法 int isLegal(int x, int y) { if (x >= 0 && x < SIZE && y >= 0 && y < SIZE && chessboard[x][y] == 0) { return 1; } return 0; } // 输出棋盘 void printChessboard() { for (int i = 0; i < SIZE; i++) { for (int j = 0; j < SIZE; j++) { printf("%2d ", chessboard[i][j]); } printf("\n"); } } // 马踏棋盘算法 int horseStep(int x, int y, int step) { int nextXPos, nextYPos; if (step == SIZE * SIZE) { // 遍历完成 printChessboard(); return 1; } for (int i = 0; i < 8; i++) { // 尝试八个方向移动 nextXPos = x + nextX[i]; nextYPos = y + nextY[i]; if (isLegal(nextXPos, nextYPos)) { chessboard[nextXPos][nextYPos] = step; if (horseStep(nextXPos, nextYPos, step + 1)) { // 递归 return 1; } chessboard[nextXPos][nextYPos] = 0; // 回溯 } } return 0; } int main() { int startX = 0, startY = 0; // 设置起始位置 chessboard[startX][startY] = 1; if (!horseStep(startX, startY, 2)) { // 从起始位置开始遍历 printf("无解!\n"); } return 0; } ``` 以上的代码使用了递归的方式实现了马踏棋盘算法。在检查下一个位置是否合法的时候,判断条件是坐标是否在棋盘内以及该位置是否已经被访问过。在遍历每一个方向的时候,将下一个位置的步数设置为当前步数加一,并递归地调用马踏棋盘函数。如果得到的结果为1,表示遍历完成,打印棋盘;如果结果为0,表示无解。 ### 回答3: 马踏棋盘问题是一个经典的回溯算法问题,可以使用C语言编写一个完整的算法来解决。 首先,我们需要定义棋盘的大小和马的初始位置。假设棋盘为8x8,初始位置为(0,0)。 然后,我们需要定义一个二维数组来表示棋盘,并将其初始化为-1,表示未走过。接下来,我们将起始位置设置为0,并从起始位置开始递归遍历棋盘。 在每个位置上,我们首先判断该位置是否越界或者已经被访问过,如果是,则返回False。否则,将该位置标记为当前步数,并且检查是否已经访问了棋盘上的每个位置(即步数是否等于棋盘大小)。 如果是,则打印出结果矩阵,并返回True。否则,我们需要按照马的移动规则进行遍历。马的移动规则是根据马字型的移动方式:横向或纵向移动2步,然后再垂直或水平移动1步。 我们将递归调用该函数来遍历下一个位置,直到找到解决方案或者遍历所有可能的路径。 最后,我们将解决方案编写成一个函数,调用该函数即可得到解决结果。 以下是一个用C语言写的完整的马踏棋盘算法的示例代码: ```c #include <stdio.h> #define SIZE 8 int board[SIZE][SIZE]; int moveX[8] = {1, 1, 2, 2, -1, -1, -2, -2}; int moveY[8] = {2, -2, 1, -1, 2, -2, 1, -1}; int solve(int x, int y, int step) { if (step == SIZE*SIZE) { for (int i = 0; i < SIZE; i++) { for (int j = 0; j < SIZE; j++) { printf("%2d ", board[i][j]); } printf("\n"); } return 1; } for (int i = 0; i < 8; i++) { int nextX = x + moveX[i]; int nextY = y + moveY[i]; if (nextX >= 0 && nextX < SIZE && nextY >= 0 && nextY < SIZE && board[nextX][nextY] == -1) { board[nextX][nextY] = step; if (solve(nextX, nextY, step+1)) { return 1; } board[nextX][nextY] = -1; } } return 0; } int main() { for (int i = 0; i < SIZE; i++) { for (int j = 0; j < SIZE; j++) { board[i][j] = -1; } } board[0][0] = 0; solve(0, 0, 1); return 0; } ``` 以上代码可以在一个8x8的棋盘上找到马踏棋盘的一条路径,并将结果输出到终端上。请注意,这个代码只会找到一条路径,可能不是唯一的解决方案。要找到所有解决方案,您可以进行一些修改来支持多个解决方案的记录和输出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值