用递归回溯法求解9*9数独的C++实现,附详细说明
本人初学,有不当之处望大神指正


代码块

//风休住于2018.8.31编写完成
//All Rights Reserved
#include<bits/stdc++.h>
using namespace std;/*数独二维数组*/
int board[9][9];/*声明Check函数,用以检查所填的数是否合法*/
bool Check(int check_number,int check_now_line,int check_now_column,int check_block_line,int check_block_column);/*输入函数*/
void Input();/*工作函数*/
bool Work(int now_line,int now_column);/*输出函数*/
void Output();/*主函数*/
int main()
{Input();if(Work(0,0)) Output();//如果有解,则调用输出函数 else cout<<"No Answer."<<endl;//如果无解,则输出“No Answer.” system("pause"); return 0;
}/*check_number为待检查的数字,check_now_line和check_now_column为待检查的坐标,
check_block_line和check_block_column为所在的3*3方格左上角坐标*/
bool Check(int check_number,int check_now_line,int check_now_column,int check_block_line,int check_block_column)
{for(int i=0;i<=8;++i)//检查与待检查的坐标同行或同列的位置 {if(board[check_now_line][i]==check_number||board[i][check_now_column]==check_number) return false;}for(int i=0;i<=2;++i)//检查与待检查的坐标同3*3方格的位置 {for(int j=0;j<=2;++j){if(board[check_block_line+i][check_block_column+j]==check_number)return false;}}return true;//通过所有检查,返回true
}void Input()
{cout<<"请输入一个9*9数独,两数见用空格隔开,空位用0表示:"<<endl; for(int i=0;i<=8;++i){for(int j=0;j<=8;++j){cin>>board[i][j];}}
}/*为使代码更为简洁,将Work函数定义为bool,如果数独有解则返回true,无解则返回false,
不用再单独定义flag来标记数独是否完成*/
bool Work(int now_line,int now_column)
{if(now_line==9){return true;//如果将数独解完,返回true }else {int next_line,next_column,block_line,block_column;next_column=now_column+1;next_line=(next_column>=9?now_line+1:now_line);next_column=(next_column>=9?0:next_column);if(board[now_line][now_column]!=0)//如果当前坐标有数字,则对下一个坐标进行工作 {if(Work(next_line,next_column)) return true;//如果数独最终有解,则不断向前返回true }else{block_line=(now_line/3)*3;//计算所在的3*3方格左上角坐标block_column=(now_column/3)*3;for(int i=1;i<=9;++i){if(Check(i,now_line,now_column,block_line,block_column)){board[now_line][now_column]=i;//如果i值合法,则对下一个坐标进行工作if(Work(next_line,next_column)) return true;}}board[now_line][now_column]=0;//回溯操作 return false;//如果i的值为1-9均不合法,则返回上一层继续循环 }}
}void Output()
{cout<<endl<<endl;for(int i=0;i<=8;++i){for(int j=0;j<=8;++j){cout<<setw(2)<<board[i][j];if((j+1)%3==0) cout<<" ";}cout<<endl;if((i+1)%3==0) cout<<endl;}
}

说明

数独规则
数独是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复。—— [ 百度百科 ]

首先对Check函数进行一下说明。假设正在对(2,4)号方块进行检查,首先需保证与(2,4)同一行及同一列上的方格中,没有与待填数字相同的方格;其次,需保证在图中标绿色的3*3方格中,没有与待填数字相同的方格,代码实现为

for(int i=0;i<=8;++i)
{if(board[check_now_line][i]==check_number||board[i][check_now_column]==check_number) return false;
}
for(int i=0;i<=2;++i)
{for(int j=0;j<=2;++j){if(board[check_block_line+i][check_block_column+j]==check_number)return false;}
}

如果通过了以上检验,则
return true

在进行求解时,主函数调用 Work(0,0) 即从方格(0,0)处开始工作,从左到右、从上到下依次进行。
如果该方格内已经填过数,则跳过这一方格,形如
if(board[now_line][now_column]!=0) { if(Work(next_line,next_column)) return true; }
接下来开始对当前方格进行处理。为了方便 Check 函数的运行,首先将计算出当前方格所处 Block 的左上角坐标,形如
block_line=(now_line/3)*3; block_column=(now_column/3)*3;
其次,检查1-9填入当前方块中是否合法,如果合法,则不管它是否正确,直接填入当前方块中,并对下一个方块进行工作,形如
if(Check(i,now_line,now_column,block_line,block_column)){board[now_line][now_column]=i;if(Work(next_line,next_column)) return true; }
像这样运行几次后,有可能出现这种情况,即先前填写的数字有错误,导致当前方块一个数也填不了;那么就会跳出检查1-9的for循环,执行以下操作
board[now_line][now_column]=0; return false;
第一句目的是将当前方块赋0(递归回溯基本操作),第二句使函数返回false,则跳出当前层次的递归,回到上一个检查1-9的for循环;如果依然没有可填的数,则再回到上一个循环,以此类推。如果第一个方格也无数可填,则主函数中的返回值为false,执行
cout<<"No Answer."<<endl;
当工作到方块(8,8)时,如果仍然找到了可填的数,则将开始对第10行进行工作。这是将会触发
if(now_line==9) {return true;}
这也意味着数独成功找到了一种解法,第81层递归返回true。在第80层递归收到true时,它也会返回true,以此类推,主函数中的返回值为true,执行
Output();
将数独数组输出,结束

特点及缺陷

关键代码 if(Work(next_line,next_column)) return true;
这段代码利用了C++语言简洁的特征,在对数独是否有解进行判断(上文中有具体说明)的同时,运行了Work函数,填写了整个数独。
缺陷在于,这篇代码纯粹利用了计算机运行速度快的特点,对所有空格进行了暴力枚举,并没有使用人工解数独时的技巧方法,有些遗憾。

测试数据

输入

0 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 6
0 8 7 9 6 4 2 5 0
0 1 0 4 0 8 0 2 0
0 0 0 7 0 3 0 0 0
0 3 0 5 0 6 0 8 0
0 7 2 3 8 1 9 6 0
9 0 0 0 0 0 0 0 3
0 0 0 0 0 0 0 0 0

输出

2 9 6 1 7 5 4 3 8
4 5 1 8 3 2 7 9 6
3 8 7 9 6 4 2 5 1
6 1 5 4 9 8 3 2 7
8 2 9 7 1 3 6 4 5
7 3 4 5 2 6 1 8 9
5 7 2 3 8 1 9 6 4
9 6 8 2 4 7 5 1 3
1 4 3 6 5 9 8 7 2

输入

0 6 0 0 0 0 0 0 0
0 0 0 0 0 1 0 6 4
5 0 0 0 3 0 7 0 0
0 0 8 0 1 6 2 0 7
2 0 0 5 0 4 0 0 9
3 0 5 8 9 0 6 0 0
0 0 7 0 2 0 0 0 3
8 4 0 7 0 0 0 0 0
0 0 0 0 0 0 0 7 0

输出

9 6 1 4 8 7 5 3 2
7 8 3 2 5 1 9 6 4
5 2 4 6 3 9 7 1 8

4 9 8 3 1 6 2 5 7
2 1 6 5 7 4 3 8 9
3 7 5 8 9 2 6 4 1

6 5 7 1 2 8 4 9 3
8 4 9 7 6 3 1 2 5
1 3 2 9 4 5 8 7 6

递归回溯求解数独 C++实现方法相关推荐

  1. 迷宫问题的三种求解方法(递归求解、回溯求解和队列求解)

    目录 一.迷宫问题的三种求解方法 递归求解 回溯求解 队列求解 二.华为迷宫问题 一.迷宫问题的三种求解方法 在迷宫问题中,给定入口和出口,要求找到路径.本文将讨论三种求解方法,递归求解.回溯求解和队 ...

  2. 求解数独难题, Sudoku问题(回溯)

    Introduction : 标准的数独游戏是在一个 9 X 9 的棋盘上填写 1 – 9 这 9 个数字,规则是这样的: 棋盘分成上图所示的 9 个区域(不同颜色做背景标出,每个区域是 3 X 3 ...

  3. 递归回溯法求数独全部解

    项目介绍 QT5做的数独求解程序,可以判断数独解的个数(如果非唯一解). 运行截图 源码说明 使用MSVC + QT5平台,故* .cpp和* .h文件均采用UTF8 + BOM编码.如果切换到Min ...

  4. php生成迷宫图片,PHP实现基于回溯法求解迷宫问题的方法详解

    本文实例讲述了PHP实现基于回溯法求解迷宫问题的方法.分享给大家供大家参考,具体如下: 引言 最近在leetcode上看了些算法题,有些看着很简单的很常用的东西,竟然一下子想不出来怎么求解,比如说:实 ...

  5. 回溯法求解数独问题(最简单,通俗易懂,附C++代码)

    问题描述:数独是一种运用纸.笔进行演算的逻辑游戏.玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行.每一列.每一个九宫格内的数字均含1-9,不重复 要求:设计算法随机生成不同 ...

  6. 利用python,求解数独

    import numpy as np import time time1 = time.time() '''整体灵感就是1 求出每个数字为0的位置可以填的数,并将其位置和能填的数分别以key和valu ...

  7. python图像数独_Python图像识别+KNN求解数独的实现

    Python-opencv+KNN求解数独 最近一直在玩数独,突发奇想实现图像识别求解数独,输入到输出平均需要0.5s. 整体思路大概就是识别出图中数字生成list,然后求解. 输入输出demo 数独 ...

  8. 软工个人项目之生成和求解数独

    软工个人项目之生成和求解数独 在这次完成个人项目的过程中,我第一次尝试了写csdn博客,用vs进行性能分析,在vs里面写单元测试,这次收获了很多.虽然还有很多需要改进的地方,但我会做得越来越好的~ 1 ...

  9. 求解数独的C++实现

    求解数独的C++实现 动机 在做数独的时候,抱着好奇心想做一个数独求解的程序. 当时我并没有接触多少算法,也是抱着试试看的心态编写了这个数独求解器. 解数独的方法 要写一个求解数独的程序,必须要线弄清 ...

  10. 【Numpy】用Python求解数独

    目录 [题目]列出如下数独问题的2种数学模型,分析两种模型的优缺点,并用Excel求解,将结果填上.这里用Python进行求解: 一.代码 model1 二.代码 model2 三.求解结果 [题目] ...

最新文章

  1. 科学处理java.lang.StackOverflowError: null异常
  2. Stack Overflow requires external JavaScript from another domain, which is blocked or failed to load.
  3. 简单的活又谈何容易呢
  4. 配合理lcd的c语言小游戏,C语言源程序LCD.doc
  5. Java核心类笔记(字符串方法、StringBuilder(Joiner)、包装、JavaBean、枚举、Math、随机数)
  6. tableview分割线
  7. linux记录iptables日志,linux – 如何配置syslog.conf文件,在单独的文件中记录iptables消息?...
  8. 抖音直播违规行为与敏感词处罚,直播必备干货大全丨国仁网络
  9. 图解WinHex使用入门
  10. URL编码的原因及场景
  11. android底部抽屉库,Xamarin.Android之简单的抽屉布局
  12. 用Regedit命令控制注册表
  13. 介词 inside like near of off past around
  14. 小学奥数 7653 地球人口承载力估计 python
  15. 2月面经:真可惜...拿了小米的offer,字节却惨挂在三面
  16. SQLiteOpenHelper 崩溃Couldnt read row 0, col -1 from CursorWindow.
  17. 错误1053 服务没有及时响应启动或控制请求
  18. Xcode Library
  19. AI上推荐 之 AFM与DIN模型(当推荐系统遇上了注意力机制)
  20. 在安卓手机上实现莫尔斯编码器

热门文章

  1. 学校官网首界面 html
  2. 教你编写一份高质量的软件测试报告
  3. 单打打法类型及技、战术特点
  4. c语言判断不是大写字母,c语言isupper()函数如何判断字符是否为大写英文字母实例...
  5. JAVA实现发短信功能
  6. 记一次授权的渗透测试
  7. 玩游戏显示服务器不稳定,FPS不稳定是什么原因?玩游戏FPS低怎么办?
  8. SI24R1引脚及软硬件中文开发资料
  9. 小米6刷peixl安卓8详细教程
  10. excel分离中英文