洛谷 数独 详细解析
数独
题目描述
数独是根据 9 × 9 9 \times 9 9×9 盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫内的数字均含 1 − 9 1 - 9 1−9 ,不重复。每一道合格的数独谜题都有且仅有唯一答案,推理方法也以此为基础,任何无解或多解的题目都是不合格的。
芬兰一位数学家号称设计出全球最难的“数独游戏”,并刊登在报纸上,让大家去挑战。
这位数学家说,他相信只有“智慧最顶尖”的人才有可能破解这个“数独之谜”。
据介绍,目前数独游戏的难度的等级有一到五级,一是入门等级,五则比较难。不过这位数学家说,他所设计的数独游戏难度等级是十一,可以说是所以数独游戏中,难度最高的等级。他还表示,他目前还没遇到解不出来的数独游戏,因此他认为“最具挑战性”的数独游戏并没有出现。
输入格式
一个未填的数独。
输出格式
填好的数独。
样例 #1
样例输入 #1
8 0 0 0 0 0 0 0 0
0 0 3 6 0 0 0 0 0
0 7 0 0 9 0 2 0 0
0 5 0 0 0 7 0 0 0
0 0 0 0 4 5 7 0 0
0 0 0 1 0 0 0 3 0
0 0 1 0 0 0 0 6 8
0 0 8 5 0 0 0 1 0
0 9 0 0 0 0 4 0 0
样例输出 #1
8 1 2 7 5 3 6 4 9
9 4 3 6 8 2 1 7 5
6 7 5 4 9 1 2 8 3
1 5 4 2 3 7 8 9 6
3 6 9 8 4 5 7 2 1
2 8 7 1 6 9 5 3 4
5 2 1 9 7 4 3 6 8
4 3 8 5 2 6 9 1 7
7 9 6 3 1 8 4 5 2
提示
2022-04-17 @farteryhr 贡献了三组 hack 数据。加入了其中两组。第三组过强(来源:https://www.dcc.fc.up.pt/~acm/sudoku.pdf),放在下边供自测。
9 0 0 8 0 0 0 0 0
0 0 0 0 0 0 5 0 0
0 0 0 0 0 0 0 0 0
0 2 0 0 1 0 0 0 3
0 1 0 0 0 0 0 6 0
0 0 0 4 0 0 0 7 0
7 0 8 6 0 0 0 0 0
0 0 0 0 3 0 1 0 0
4 0 0 0 0 0 2 0 0
输出
9 7 2 8 5 3 6 1 4
1 4 6 2 7 9 5 3 8
5 8 3 1 4 6 7 2 9
6 2 4 7 1 8 9 5 3
8 1 7 3 9 5 4 6 2
3 5 9 4 6 2 8 7 1
7 9 8 6 2 1 3 4 5
2 6 5 9 3 4 1 8 7
4 3 1 5 8 7 2 9 6
不再赘述数独游戏的规则
思路
见到这种填数题,大概率需要用到深度优先搜索,其大致思路就是暴力枚举,如果当前情况下这种填法无法满足题意,便会回溯寻找下一种情况,持续下去,一定会搜索到满足题意的情况
假设我们先填了1,然后下一个点尝试1,不成功便尝试2,一直到9,如果都不行,说明先填的1存在问题,将先填入的1改成2,这样循环下去。
由于这道题建立在数独游戏的背景之上,所以我们需要关注游戏规则。数独要求每一行,每一列,每一宫不能有同样的数字出现,可以填入的数字为1到9,那么我们可以设置四个数组
int hang[9][10]={0},
lie[9][10]={0},
pand[9][10]={0},//即数字盘,先初始化为0
gong[3][3][10] = {0};
其中,hang[x][i] = 0
代表i这个数字在第x行没有被填过,lie[y][i] = 0
代表i这个数字没有在第y列出现过
需要注意的是对于宫数的判断,我可以举一个例子,我们数盘第一行第一列的数字坐标为(0,0)
那么对于第一行第三列的数字来说,它的坐标为(0,2)
。为什么纵坐标为2而不是3呢?因为我们的起始坐标为0,对于这个点来说它属于第一宫,对应gong[0][0]
,他紧接着右边的点属于第二宫,也就是说(0,3)对应第二宫,也就是gong[0][1]
所以我们可以得到宫数组的定义和表达式
gong[x/3][y/3][i] = 0
代表着i这个数字没有在对应宫出现过。这里的x和y不会等于9,因为x是从零开始,最多等于8
既然要用到深度优先搜索,我们需要先考虑回溯的条件,我们可以确定横坐标然后搜索纵坐标。如果正好完成了整个数独,刚好填入最后一个数字的时候,也就是临界情况为x == 8 && y == 8
,我们已经确定了搜索思路,那么完成后,紧接着x和y对应的值就是x == 8 && y == 9
,这种情况下就应该直接输出整个数盘
if(x == 8 && y == 9 ){for(int i = 0; i < 9; i++){for(int j = 0; j < 9; j ++){cout<<pand[i][j]<<" ";}cout<<endl;}exit(0);}
如果我们把exit(0)换成return,那么会有一个测试点超时!
我们可以思考这个过程,在完成全部填入之前,x相对于y变化很是缓慢,那么我们就先考虑y到临界的情况,在y为8时,填入数字后,根据顺序,y的值便成了9,那么这个时候就应该在下一列的第一行填入数字,代码如下
if(y == 9){ dfs(x+1,0);}
对于正常情况,因为在填入过程中会有回溯过程,所以我们应该判断这个点是否存在数字,如果存在,那么跳过这个点就行,如果没有,我们需要先判断填入的数字是否在行,列,宫出现过,如果出现,就跳过,如果没有,就进行正常填入,然后进行回溯。
因为这是一个暴力搜索的过程,所以我们可以从1开始填入
else{if(pand[x][y] != 0){dfs(x,y+1);}if(pand[x][y] == 0){for(int n = 1; n <= 9; n++){if(!hang[x][n] && !lie[y][n] && !gong[x/3][y/3][n]){pand[x][y] = n;hang[x][n] = 1;lie[y][n] = 1;gong[x/3][y/3][n] = 1;dfs(x,y+1);hang[x][n] = 0;lie[y][n] = 0;gong[x/3][y/3][n] = 0;pand[x][y] = 0;}}}}
!hang[x][n] && !lie[y][n] && !gong[x/3][y/3][n]
这段代表n这个没有在行,列,宫出现过。
为什么要把填入数字盘的数字也抹掉呢?对应pand[x][y] = 0;
就是因为最开始说的,如果这个数字不合适,就会回溯然后寻找新的可能。换句话说,先前填入的数字不一定是正确的,如果发现填不下去,就会返回。
因为题目中给了部分数字,所以我们在main函数里面需要对这些数字进行操作
int main()
{for(int i = 0; i < 9; i++){for(int j = 0;j < 9;j++){cin>>pand[i][j];if(pand[i][j] > 0){hang[i][pand[i][j]]=1; //pand[i][j]代表的是一个数lie[j][pand[i][j]] = 1;gong[i/3][j/3][pand[i][j]] = 1;} }}dfs(0,0);return 0;}
对题目中已经给的数字,相当于已经出现过了,让对应行列宫的值等于一即可。
完整代码:
#include<iostream>
#include<algorithm>
using namespace std;
int hang[9][10]={0},lie[9][10]={0},pand[9][10]={0},gong[3][3][10] = {0};
void dfs(int x,int y){if(x == 8 && y == 9 ){for(int i = 0; i < 9; i++){for(int j = 0; j < 9; j ++){cout<<pand[i][j]<<" ";}cout<<endl;}exit(0);}if(y == 9){ dfs(x+1,0);}else{if(pand[x][y] != 0){dfs(x,y+1);}if(pand[x][y] == 0){for(int n = 1; n <= 9; n++){if(!hang[x][n] && !lie[y][n] && !gong[x/3][y/3][n]){pand[x][y] = n;hang[x][n] = 1;lie[y][n] = 1;gong[x/3][y/3][n] = 1;dfs(x,y+1);hang[x][n] = 0;lie[y][n] = 0;gong[x/3][y/3][n] = 0;pand[x][y] = 0;}}}}}
int main()
{for(int i = 0; i < 9; i++){for(int j = 0;j < 9;j++){cin>>pand[i][j];if(pand[i][j] > 0){hang[i][pand[i][j]]=1;lie[j][pand[i][j]] = 1;gong[i/3][j/3][pand[i][j]] = 1;} }}dfs(0,0);return 0;}
有不清楚或者错误的地方请指出
洛谷 数独 详细解析相关推荐
- 洛谷——P1597 语句解析(两种解法)
P1597 语句解析(两种解法) 题目背景 木有背景-- 题目描述 一串长度不超过 255 的 PASCAL 语言代码,只有 a,b,c 3 个变量,而且只有赋值语句,赋值只能是一个一位的数字或一个变 ...
- [洛谷P1074] 靶形数独
洛谷题目链接:靶形数独 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博 ...
- 倍增:喷泉 深度解析(洛谷P7167)
洛谷传送门 解析 什么破题 数据范围来看很明显最多到nlogn 首先,对于样例进行一下分析: 我们可以把它转化为一棵树: 每一个根都有对应的权,给你一个结点和加权和,问能爬到哪里 既然是树,自然要找爸 ...
- 洛谷P1074 靶形数独 [搜索]
题目传送门 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z 博士拿出了 ...
- 洛谷 10月 csp-s 模拟赛 T1,T2解析及代码
洛谷 10月 csp-s 模拟赛 T1,T2解析及代码 T1 Magenta Potion 题目描述 给定一个长为 nnn 的整数序列 aaa,其中所有数的绝对值均大于等于 222.有 qqq 次操作 ...
- 洛谷题目---P1784 数独
题目描述 数独是根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行.每一列.每一个粗线宫内的数字均含1-9,不重复.每一道合格的数独谜题都有且仅有唯一答案,推理方法也以此为基础,任何无 ...
- 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释...
P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...
- 【洛谷】P1328 [NOIP2014 提高组] 生活大爆炸版石头剪刀布(详细代码)
[洛谷]石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头.如果两个人出拳一 样,则不分胜负.在<生活大爆炸>第二季第8集中出现了一种石头剪刀布的升级版游戏. 1.[题目描述] 2 ...
- 【洛谷】 P1424 小鱼的航程(改进版)(详细代码)
[洛谷]有一只小鱼,它平日每天游泳 250 公里,周末休息,假设从周 x 开始算起,过了 n天以后,小鱼一共累计游泳了多少公里呢? 1.[题目描述] 2.[代码] 1.[题目描述] 题目背景 原来的题 ...
最新文章
- Python组合数据类型之序列类型
- 实现不可变类如何禁止子类化?
- .重要开源协定的比较以及区别
- 开年趣图汇总:对不起,让你笑了这么久
- 将一个16进制数转化为10进制数
- [Node.js] 模块化 -- http服务器模块
- js面向对象的程序设计 --- 上篇(理解对象)
- 常见数通设备镜像制作模板
- 抓linux肉鸡教程视频,超简单的菜鸟网吧抓肉鸡教程
- qss设置平面按钮_QToolButton设置QSS
- 开源3D建模软件FreeCAD基础介绍与入门
- 常用颜色的RGB值及中英文名称
- 计算机桌面空白图标如何删除,桌面上有两个i空白文件的图标删不掉怎么办急急急...
- apache beam java api_Apache Beam的基本概念
- 2018清明假期旅游预测报告:全国游客人次预计破亿
- python delphi 比较_Python4Delphi注意事项
- Minecraft多人联机服务器配置
- mysql5.7.20 安装过程记录
- week3note函数
- 几个简单实用的vbs命令