c++程序求解9X9数独

2023-11-01 23:20
文章标签 c++ 程序 求解 数独 9x9

本文主要是介绍c++程序求解9X9数独,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

也算是机缘巧合,在做力扣第三十六题有效的数独的时候没理解好题目意思,以为是要求解数独,正好我也觉得挺有意思,分享一下

解题方法主要使用回溯法,大致意思就是对于任意一个可填数字的位置,遍历所有可以填入的数字,填入数字后递归调用去下一个可填数字位置,当所有位置都被填满就找到了一个解

以下是我学习回溯法时的记录

是一种组织的井井有条的,能避免不必要搜索的穷举式搜索法

是在问题的解空间树中按深度优先策略从根结点触发搜索整个解空间树
对于每一点都要判断这一结点是否包含问题的解,如不包含则对兄弟节点搜索,包含才进入该子树继续深度优先策略搜索

回溯法希望问题的解可以表示为一个n元组{x1,x2,x3......xn}  解空间树  就是解所有的可能组成一棵树

                            A                                第一层是第一个物品  左边代表放入,右边代表不放入  0
                    1/                \0
                B                        C                    第二层是第二个物品                                  1
            /        \                /        \
        D                E        F                G            第三层是第三个物品                                  2
    /        \        /      \   /        \        /        \
   H        I        J      K      L        M        N         O                                                          3

但是这么看起来很多不是吗,回溯法为了避免生成不可能产生最佳解的问题状态,有一个剪枝函数来处死那些解以减少计算量

常用两类剪枝函数:用 约束函数 在扩展结点(还在生成儿子)处剪去不满足约束的结点、用 上界函数 剪去得不到最优解的子树
对于子集树这两个方法分别应用于左右分支  对于排列树(每一个分支性质一样)同时应用于每一个分支  但在我看来不是那么严格

回溯法在搜索过程中动态产生解空间树,即边搜边拓展
任何时候仅记录根到当前节点的路径

void backtrack(int t){  n是总层数或者说总个数
    if(t>n){  t>n说明已经走到最下层以下了,那说明这个解已经构造完成  就像上面已经是4了,超过了最下层
        output(x);
    }else{
        for(int i=f(n,t); i<=g(n,t); i++){  f(n,t)是左分支编号,g(n,t)代表右分支编号,也就是从左分支遍历到右分支,把每一个分支拓展出来
            x[t] = h(i);
            if(constraint(t)&&bound(t)) backtrack(t+1);  这个结点是否满足要求
        }
    }
}

常见的两种解空间树:选择树和排列树、其实还有个n叉树,具体看图的m着色问题  他们都是n+1层      
选择树每个结点下面有两个分支                    排列树,第一层往下3个,第二层往下排两个,第三层往下排一个

          A                                    A

   1/        \0                       1/    2|     \3 

  B          C                    B        C        D    看最左边,123三个元素,如果上面选择排了1,比如AB
 1/    \0        1/ \0       2/\3       1/\3       1/\2  那么下面B往下排就只能排2或者3,比如BE这条线

D    E        F    G        E  F    G  H    I  G  再往下走因为AB排了1,BE排了2,那么EK就只能排3
                                 3|  |2  3|  |1  2|  |1
                                 K  L    M  N    O  P

关于算法的解题方法我推荐看中国大学mooc的算法设计与分析

青岛大学的那个,貌似贴截图会违规,各位自己找下吧,教授讲的超棒

具体包括回溯法、分支限界法、分治法、动态规划法等讲解

#include <vector>
#include <iostream>
namespace 有效的数独 {class Solution {public:std::vector<std::vector<std::vector<char>>> save;bool find = false;bool isValidSudoku(std::vector<std::vector<char>>& board) {int i = 0, j = 0;while (board[i][j] != '.') {if (j < board.size() - 1) { j++; continue;}if (i < board.size() - 1) { i++; j = 0; continue;}}BackTrack(board, i, j);if (find) { return true; };return false;}void BackTrack(std::vector<std::vector<char>>& board, int i, int j) {for (int x = 1; x <= board.size(); x++) {  //  找数放if (Place(board, i, j, x)) {  //  还能找到一个可以放if (i == board.size() - 1 && j == board.size() - 1) {find = true;save.push_back(board);return;}int temp1 = i, temp2 = j;while (board[i][j] != '.') {if (j < board.size() - 1) { j++; continue; }if (i < board.size() - 1) { i++; j = 0; continue; }find = true;save.push_back(board);return;}BackTrack(board, i, j);board[i][j] = '.';//指针得回到上一个位置i = temp1;j = temp2;}}}bool Place(std::vector<std::vector<char>>& board, int i, int j, int x) {//  行列不重复for (int a = 0; a < board.size(); a++) {if (board[i][a] == x + '0' || board[a][j] == x + '0') {//发现重复return false;}}//  3x3内不重复int startrow = i / 3 * 3, startcol = j / 3 * 3;for (int m = 0; m <= 2; m++) {for (int n = 0; n <= 2; n++){if (board[startrow + m][startcol + n] == x + '0') {return false;}}}board[i][j] = x + '0';return true;}};}
int main() {std::vector<std::vector<char>> a ={ {'.', '3', '.', '.', '7', '.', '.', '.', '.'},{'6', '.', '.', '1', '.', '5', '.', '.', '.'},{'.', '9', '8', '.', '.', '.', '.', '6', '.'},{'8', '.', '.', '.', '6', '.', '.', '.', '3'},{'.', '.', '.', '8', '.', '3', '.', '.', '1'},{'7', '.', '.', '.', '2', '.', '.', '.', '.'},{'.', '6', '.', '.', '.', '.', '2', '8', '.'},{'.', '.', '.', '4', '.', '9', '.', '.', '5'},{'.', '.', '.', '.', '.', '.', '.', '7', '.'} };有效的数独::Solution s;if (s.isValidSudoku(a)) {std::cout<<"有"<< s.save.size()<<"个解" << std::endl;for (int t = 0; t <s.save.size() ; t++){std::cout << "第" << t+1 << "个解:" << std::endl;for (int i = 0; i < a.size(); i++) {for (int j = 0; j < a.size(); j++){std::cout << s.save[t][i][j] << " ";}std::cout << std::endl;}std::cout << std::endl;}}else{std::cout << "无解";}}

输入待解数独后的输出

‘.’的位置代表这里是空的,还没有填入数字

这篇关于c++程序求解9X9数独的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/326481

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

EMLOG程序单页友链和标签增加美化

单页友联效果图: 标签页面效果图: 源码介绍 EMLOG单页友情链接和TAG标签,友链单页文件代码main{width: 58%;是设置宽度 自己把设置成与您的网站宽度一样,如果自适应就填写100%,TAG文件不用修改 安装方法:把Links.php和tag.php上传到网站根目录即可,访问 域名/Links.php、域名/tag.php 所有模板适用,代码就不粘贴出来,已经打