基于回溯思想的C++数独求解器

       数独(日语:数独,罗马音:sūdoku,意为“数字单一”;英语发音:/suːˈdoʊkuː/、 /-ˈdɒk-/、 /sə-/ ,最初被称为“数字填空”)是一种基于逻辑的组合式数字填充谜题。在经典数独中,目标是将数字填入一个 9×9 的网格中,使得每一列、每一行以及组成该网格的九个 3×3 子网格(也称为“宫”“区块”或“区域”)都包含从 1 到 9 的所有数字。谜题设计者会提供一个部分填充好的网格,对于一个设计合理的谜题,该网格只有唯一解。

代码讲解:

1. 头文件与命名空间

#include <array>
#include <iostream>

namespace backtracking {
    namespace sudoku_solver {
        // 函数定义
    }
}

作用

  • <array>:提供对std::array的支持,用于存储数独的9x9网格。

  • <iostream>:用于输入输出操作。

  • 命名空间backtrackingsudoku_solver将代码逻辑分组,避免命名冲突,提高可读性

2. isPossible 函数

template <size_t V>
bool isPossible(const std::array<std::array<int, V>, V>& mat, int i, int j, int no, int n) {
    // 检查行和列
    for (int x = 0; x < n; x++) {
        if (mat[x][j] == no || mat[i][x] == no) return false;
    }
    // 检查3x3子网格
    int sx = (i / 3) * 3, sy = (j / 3) * 3;
    for (int x = sx; x < sx + 3; x++) {
        for (int y = sy; y < sy + 3; y++) {
            if (mat[x][y] == no) return false;
        }
    }
    return true;
}
  • 作用
    验证在位置(i, j)填入数字no是否合法。

  • 细节

    • 行和列检查:遍历当前行和列,确保no未出现过。

    • 子网格检查:计算当前单元格所在的3x3子网格起始位置,遍历该子网格检查重复。

    • 若所有检查通过,返回true,否则返回false

3. printMat 函数

template <size_t V>
void printMat(const std::array<std::array<int, V>, V>& mat, 
             const std::array<std::array<int, V>, V>& starting_mat, int n) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (starting_mat[i][j] != mat[i][j]) { // 高亮新填入的数字
                std::cout << "\033[93m" << mat[i][j] << "\033[0m ";
            } else {
                std::cout << mat[i][j] << " ";
            }
            if ((j + 1) % 3 == 0) std::cout << '\t'; // 列分隔符
        }
        if ((i + 1) % 3 == 0) std::cout << std::endl; // 行分隔符
        std::cout << std::endl;
    }
}
  • 作用
    打印数独矩阵,高亮显示求解过程中填入的新数字。

  • 细节

    • 使用ANSI转义码\033[93m将新数字显示为黄色。

    • 每3列添加制表符,每3行换行,增强可读性。

4. solveSudoku 函数

template <size_t V>
bool solveSudoku(std::array<std::array<int, V>, V>& mat,
                const std::array<std::array<int, V>, V>& starting_mat, int i, int j) {
    // 基准情况:处理完所有行
    if (i == 9) {
        printMat<V>(mat, starting_mat, 9);
        return true;
    }
    // 处理完当前行,转至下一行
    if (j == 9) return solveSudoku<V>(mat, starting_mat, i + 1, 0);
    // 跳过已填数字
    if (mat[i][j] != 0) return solveSudoku<V>(mat, starting_mat, i, j + 1);
    // 尝试填入1-9
    for (int no = 1; no <= 9; no++) {
        if (isPossible<V>(mat, i, j, no, 9)) {
            mat[i][j] = no;
            if (solveSudoku<V>(mat, starting_mat, i, j + 1)) return true;
            mat[i][j] = 0; // 回溯
        }
    }
    return false;
}
  • 作用
    递归回溯求解数独。

  • 细节

    • 基准情况:当i == 9时,所有行已处理完毕,打印结果并返回true

    • 列越界处理:当j == 9时,转至下一行首列。

    • 跳过已填格:若当前格已有数字,直接处理下一格。

    • 尝试填入数字:循环尝试1-9,若合法则递归处理下一格。

    • 回溯机制:若后续递归失败,重置当前格为0,继续尝试其他数字。

5. main 函数

int main() {
    const int V = 9;
    std::array<std::array<int, V>, V> mat = { /* 初始数独数据 */ };
    backtracking::sudoku_solver::printMat<V>(mat, mat, 9);
    std::cout << "Solution " << std::endl;
    std::array<std::array<int, V>, V> starting_mat = mat;
    backtracking::sudoku_solver::solveSudoku<V>(mat, starting_mat, 0, 0);
    return 0;
}
  • 作用
    初始化数独问题并启动求解。

  • 细节

    • 定义初始数独矩阵mat,包含预填数字和空白(0)。

    • 打印初始状态,调用solveSudoku(0, 0)开始求解。

总结

  • 算法核心:回溯法,通过递归尝试所有可能数字,遇到矛盾时回溯。

  • 关键优化isPossible函数确保每一步的合法性,减少无效搜索。

  • 用户体验printMat通过颜色区分原始数字和新填数字,提升可读性。

  • 适用性:针对标准9x9数独设计,代码结构清晰,易于扩展(如支持其他尺寸需调整子网格计算逻辑)。

最终结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值