C++骑士游历问题(马踏棋盘)解析

C++骑士游历问题(马踏棋盘)解析

简介

骑士游历问题,又称马踏棋盘问题,属于图论中的路径问题。问题描述:在一个 n*n 的棋盘上,放置一个马的棋子,从任意一个位置出发,按照马的走法,遍历所有的棋盘。不可重复经过。

解题思路

递归回溯法

定义

首先定义一个二维棋盘 board 存储马在棋盘上的路径。board[i][j]的值为k表示是第 k 步走到了位置 (i, j)。对于第一步,令 board[i][j]=1。

使用递归回溯法,对于当前位置 (x,y),做以下几个步骤:

  1. 判断此位置是否越界,如果越界,则返回 false。
  2. 判断此位置是否已经走过,如果走过,则返回 false。
  3. 如果当前步数已经到达 n*n,表示完成遍历,返回 true。
  4. 对当前位置各个方向上的下一步位置递归调用步骤 1~3,如果返回值为 true,表示此方向可以到达,更新 board 数组,在把当前位置返回到它之前的步骤栈中。
bool knight_travel(int x, int y, int step, int n, vector<vector<int>>& board){
    // 判断越界情况
    if (x < 0 || x >= n || y < 0 || y >= n){
        return false;
    }
    // 判断已经走过的情况
    if (board[x][y] != 0){
        return false;
    }
    // 当前步数已经到达 n*n,表示完成遍历
    if (step == n * n){
        return true;
    }
    // 对当前位置各个方向上的下一步位置递归调用步骤 1~3
    int next_x;
    int next_y;
    for (int i = 0; i < 8; i++){
        next_x = x + dx[i];
        next_y = y + dy[i];
        if (knight_travel(next_x, next_y, step + 1, n, board)){
            board[next_x][next_y] = step + 1;
            return true;
        }
    }
    // 没有找到可行解,返回 false
    return false;
}

示例

以 n = 8 为例,输出一个 8*8 的棋盘,其中 1 表示起始点,64 表示终点,其余数字表示到达此位置的步数。

int main(){
    int n = 8;
    vector<vector<int>> board(n, vector<int>(n, 0));
    int start_x = 0;
    int start_y = 0;
    board[start_x][start_y] = 1;
    if (knight_travel(start_x, start_y, 1, n, board)){
        for (int i = 0; i < n; i++){
            for (int j = 0; j < n; j++){
                cout << board[i][j] << "\t";
            }
            cout << endl;
        }
    }
    return 0;
}

输出结果为:

1       38      59      48      53      28      43      62
58      47      2       37      60      63      52      27
39      54      49      58      45      26      29      42
46      57      36      3       44      61      26      25
55      40      57      50      23      24      41      30
34      45      56      35      4       31      22      19
59      22      41      62      11      20      9       32
44      33      24      17      56      51      18      5

Warnsdorff算法

定义

Warnsdorff算法是一种贪心算法,其主要思想是每次都优先选择下一步可以到达空位最少的位置。

算法具体实现步骤:

  1. 从起始位置开始,将当前可行位置的下一步空位数量预先计算出来,并排序(升序),选择空位最少的位置为下一步走的位置。
  2. 访问下一步位置,将当前可行位置的下一步空位数量重新计算,按照空位数量排序后,选择空位最少的位置为下一步走的位置。依次类推直到遍历完整个棋盘。
vector<int> dx = {1, 1, 2, 2, -1, -1, -2, -2};
vector<int> dy = {2, -2, 1, -1, 2, -2, 1, -1};

bool within_board(int x, int y, int n){
    return (x >= 0 && x < n) && (y >= 0 && y < n);
}

bool get_next_move(int x, int y, int n, vector<vector<bool>>& visited, vector<pair<int,int>>& next_moves){
    for (int i = 0; i < 8; i++){
        int next_x = x + dx[i];
        int next_y = y + dy[i];
        int count = 0;
        if (within_board(next_x, next_y, n) && !visited[next_x][next_y]){
            for (int j = 0; j < 8; j++){
                int t_x = next_x + dx[j];
                int t_y = next_y + dy[j];
                if (within_board(t_x, t_y, n) && !visited[t_x][t_y]){
                    count++;
                }
            }
            next_moves.push_back({count, i});
        }
    }
    if (next_moves.empty()){
        return false;
    }
    sort(next_moves.begin(), next_moves.end());
    return true;
}

void print_board(vector<vector<int>>& board){
    int n = board.size();
    for (int i = 0; i < n; i++){
        for (int j = 0; j < n; j++){
            cout << board[i][j] << "\t";
        }
        cout << endl;
    }
}

void knight_travel_w(vector<vector<int>>& board){
    int n = board.size();
    vector<vector<bool>> visited(n, vector<bool>(n, false));
    int x = 0;
    int y = 0;
    int step = 1;
    visited[x][y] = true;
    board[x][y] = step;
    while (step < n * n){
        vector<pair<int,int>> next_moves;
        if (!get_next_move(x, y, n, visited, next_moves)){
            // 无解情况
            return;
        }
        auto next_move = next_moves[0];
        int next_x = x + dx[next_move.second];
        int next_y = y + dy[next_move.second];
        step++;
        visited[next_x][next_y] = true;
        board[next_x][next_y] = step;
        x = next_x;
        y = next_y;
    }
}

示例

以 n = 8 为例,输出一个 8*8 的棋盘,其中 1 表示起始点,64 表示终点,其余数字表示到达此位置的步数。

int main(){
    int n = 8;
    vector<vector<int>> board(n, vector<int>(n, 0));
    knight_travel_w(board);
    print_board(board);
    return 0;
}

输出结果为:

1       58      45      34      19      8       31      50
44      33      2       57      32      49      20      9
59      46      35      18      7       30      51      38
42      43      56      3       50      61      10      21
27      60      47      36      53      4       37      52
16      41      22      55      62      17      24      11
39      28      63      48      15      6       23      40
64      25      40      29      54      43      12      5

总结

两种方法的时间复杂度都是指数级别的,对于比较大的 n 值,都会出现栈溢出等问题,因此需要优化算法,使其不再具有指数级别的复杂度。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++骑士游历问题(马踏棋盘)解析 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • C++实现简单酒店管理系统

    C++实现简单酒店管理系统攻略 简介 C++实现简单酒店管理系统是一个典型的控制台应用程序,用于对酒店客房进行预定、入住、退房、查询、统计等操作。 设计 整个酒店管理系统可以分为以下几个部分: 客房类型 客房类型编号 客房类型名称 客房单价 客房信息 客房编号 客房类型 客房状态(已预订、已入住、空闲) 入住人姓名 入住人电话 入住日期 离店日期 订单信息 …

    C 2023年5月23日
    00
  • C程序 用函数显示两个区间的素数

    下面是“C程序 用函数显示两个区间的素数”的完整使用攻略。 1.功能介绍 此程序通过定义一个函数来显示两个区间内的素数。输入两个整数,程序将找到这两个整数之间所有的素数,并显示出来。 2. 使用方法 2.1 下载程序 将程序的代码复制到你的集成开发环境(IDE)中,并保存到c文件中,例如:prime_numbers.c 2.2 定义输入 在程序的main函数…

    C 2023年5月9日
    00
  • 三星Galaxy Book Flex值得入手吗 三星笔记本Galaxy Book Flex详细评测

    三星Galaxy Book Flex值得入手吗 三星笔记本Galaxy Book Flex详细评测 如果你正在寻找一款高性能、轻巧、功能强大的2合1笔记本,那么三星Galaxy Book Flex绝对值得一看。该笔记本采用最新一代的处理器,配备高清触摸屏和可旋转键盘,具备出色的性能和灵活的使用方式,让你随时随地体验高效便捷的计算体验。 性能和硬件 三星Gal…

    C 2023年5月22日
    00
  • C语言实现24点游戏计算器的示例代码

    C语言实现24点游戏计算器的示例代码 1. 需求分析 本游戏需要实现的功能有:1. 生成指定数量的随机数2. 针对生成的数字进行四则运算3. 检查计算结果是否等于24,并输出计算过程 2. 示范代码 下面是C语言实现24点游戏计算器的示例代码: #include <stdio.h> #include <stdlib.h> #inclu…

    C 2023年5月23日
    00
  • C语言自定义函数的实现

    C语言中自定义函数的实现可以分为以下几个步骤: 函数声明 : 在使用函数之前,需要先声明函数。函数声明分为两种,一种是函数原型声明,另一种是直接写函数定义。 函数定义:函数定义包括函数名、入参、返回值和函数体。其中函数体是自定义函数的核心部分。 函数调用:调用自定义函数需要使用函数名,并传递相应的参数,等待函数返回相应的结果。 下面,我们用两个示例来说明自定…

    C 2023年5月23日
    00
  • Java8新特性:函数式编程

    Java8新特性:函数式编程 在Java8中,函数式编程成为了一项重要的新特性。函数式编程的核心思想是将函数作为一等公民来处理,这意味着函数可以被当做参数传递,也可以被当做返回值返回。Java8通过引入函数接口、Lambda表达式、方法引用等特性来支持函数式编程。 函数接口 函数接口是函数式编程的关键组件之一,它是一个只有一个抽象方法的接口。Java8中提供…

    C 2023年5月23日
    00
  • C++文件读写代码分享

    C++文件读写代码分享 在C++中,可以通过文件读写来实现将程序处理过的数据存储起来,或者是从外部文件读取数据。本文将介绍C++中文件读写的相关内容,包括文件的打开、读写、关闭等操作,同时提供两个示例供参考。 文件的打开与关闭 文件的打开与关闭是文件读写操作的前提,只有先打开文件,才能够进行文件的读写,读写完成后,还要关闭文件,以释放文件系统资源。 打开文件…

    C 2023年5月24日
    00
  • C语言实现猜数游戏

    C语言实现猜数游戏攻略 一、简介 C语言实现猜数游戏是一种比较简单的小项目,它可以帮助初学C语言的程序员更好地理解C语言的基本语法,提升程序设计能力。本攻略将介绍实现猜数游戏的完整过程,并提供两个示例。 二、游戏规则 猜数游戏的规则非常简单,程序先生成一个1~100之间的随机整数,玩家需要在规定的次数内猜出这个数字。每次猜数后,程序会根据玩家的猜测结果给出提…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部