程序员休闲娱乐之数独!| 技术头条
作者 | 宋广泽
责编 | 胡巍巍
前几天闲着没事,就玩了玩经典益智游戏数独。
简单的可以轻松过关。
稍微难一点的也可以勉强过关。
可是再难呢?如下图所示这种的,就有点招架不住了。
有的时候凭直觉相信自己填的数字是正确的,可是填着填着快到了最后,却发现前面的某个数字填错了导致后面的也一错再错,根本无法修补,颇有些不撞南墙不回头的意味在里面。
计算机总是能够不知疲倦的替你思考,何不让计算机撞南墙然后最后给你答案?
身为一名三流算法竞赛选手,第一时间想到了深度优先搜索算法,也就是常说的DFS算法,这种算法的思想就是一条路走到黑,发现走的不对再退回上一个十字路口选择另一条没有尝试过的路走,如果问题存在一个解,那么总有那么一个时刻可以找到这个解。
DFS虽然不是一个多项式时间的算法,但对于解决数独问题来说足够了。
因为传统数独游戏一定是在9*9=81个方格内填数字,不存在更大的问题规模,所以大家就不用担心DFS会花费很长的时间来解决数独问题。
当然大家也不用担心环境问题,简单的C++编译环境、控制台应用程序就可以完成。
先简单介绍以下DFS算法:
如上图所示,我们的起点是A,我们要找到一条到H点的路,我们让遍历的顺序为从左至右。
从A出发,因该先到达B,B再到达D,发现D没有子结点,撞了南墙,该回头了;
于是又回退到B,E还没有搜索过,就搜索E,E又到I,又撞了南墙,回头到B,B也没有其他欸有走过的路径了,再回退到A;
A还可以从C开始搜索,C先到F,撞南墙,回退到C;再搜索G,撞南墙,回退到C;搜索H,此时才找到了H,也找到了一条从A到H的路径即A->C->H。
对于数独,首先要解决的就是数独规则的判定,所有的数字只有1-9,每一行、每一列、等分的每个九宫格内都不能出现重复的数字。
采用标记法,每一行、每一列、每个九宫格都有一个长度为10的的标记数字1-9在某一行、某一列、某个九宫格中是否出现过的数组名为dp,当然,长度设为9也可以,但是数组的下标是从0开始的,因此用0标记数字1是否出现过是不符合人类习惯的。
我们将数独的9行从0到8编号,将9列从0到8编号,将九宫格按照从上往下、从左往右的顺序从0到8编号,这样在遍历时,就可以根据行列坐标找到相应的dp数组,然后看看当前坐标下的数是不是已经出现过了,如果之前已经出现过,说明数独中出现了重复的数字,直接返回false。
检查数独合法性的函数的源代码:
bool check(char **board,int boardRowSize,int boardColSize)
{
int i,j;
bool dpRow[9][10],dpCol[9][10],dpGrid[9][10];
memset(dpRow,0,sizeof(dpRow));
memset(dpCol,0,sizeof(dpCol));
memset(dpGrid,0,sizeof(dpGrid));
for(i=0;i<9;i++)
{
for(j=0;j<9;j++)
{
if(board[i][j]!='.')
{
if(dpRow[i][board[i][j]-'0']==true)
return false;
if(dpCol[j][board[i][j]-'0']==true)
return false;
if(dpGrid[i/3*3+j/3][board[i][j]-'0']==true)
return false;
dpRow[i][board[i][j]-'0']=true;
dpCol[j][board[i][j]-'0']=true;
dpGrid[i/3*3+j/3][board[i][j]-'0']=true;
}
}
}
return true;
}
举个例子,如果当前遍历到的坐标是(3,0),即第四行第一列,那么它对应的行的dp数组的编号就是行坐标3,对应的列的dp数组的编号就是列坐标0,而对应的九宫格的dp数组的编号刚好是 行坐标3/3(向下取整)*3+列坐标0/3(向下取整),得到的dp数组的编号是3(第四个九宫格)。
从树的搜索换到数独上来,在九乘九方格中,从上往下,从左往右进行遍历,每遍历到一个空的方格,就尝试填入1到9中的数字,每填入一个数字,然后检查一下有没有违反数独规则,如果违反了则说明此路不通,回退到上一个状态;
如果没有违反数独规则,则继续向下一个空的方格尝试填入1到9中的数字,如此循环往复,最终会给出一个完整的数独的解。如果填入1-9都不是合法的,则说明数独无解。
搜索并尝试填入数字的函数的源代码:
bool dfs(char **board,int boardRowSize,int boardColSize)
{
int i,j,k;
bool flag=false;
for(i=0;i<boardRowSize;i++)
{
for(j=0;j<boardColSize;j++)
{
if(board[i][j]=='.')
{
for(k=1;k<=9;k++)
{
board[i][j]=k+'0';
if(check(board,boardRowSize,boardColSize))
{
flag=dfs(board,boardRowSize,boardColSize);
if(flag==true)
return true;
}
board[i][j]='.';
}
return false;
}
}
}
return true;
}
为了代码的可移植性,传入的参数是一个表示数独地图的二级指针、总行数和总列数。
在主函数中,可以先为数独开辟内存空间,然后以字符串的形式输入原始条件。若输入有误则极有可能出现填不全的结果,为了避免这种结果我们可以加一条提示,输入有误则输出无法求解,输入正确才输出正确列。
主函数源代码:
int main()
{
int i;
board=(char **)malloc(9*sizeof(char *));
for(i=0;i<9;i++)
{
board[i]=(char *)malloc(9*sizeof(char));
}
for(i=0;i<9;i++)
{
scanf("%s",board[i]);
}
bool flag=dfs(board,9,9);
if(flag)
{
printf("求解成功:\n");
for(i=0;i<9;i++)
{
printf("%s\n",board[i]);
}
}
else
{
printf("无法求解!\n");
}
for(i=0;i<9;i++)
{
free(board[i]);
board[i]=NULL;
}
free(board);
board=NULL;
return 0;
}
填入一局数独给出的原始条件,得到如下的结果,
再将结果填入游戏,直接通关。
忙碌了一天,算是最后给自己的一点小小的惊喜。希望本文对你有帮助哦!
附C++控制台应用程序的全部源代码:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
char **board;
//检验是否是一个有效的数独
bool check(char **board,int boardRowSize,int boardColSize)
{
int i,j;
bool dpRow[9][10],dpCol[9][10],dpGrid[9][10];
memset(dpRow,0,sizeof(dpRow));
memset(dpCol,0,sizeof(dpCol));
memset(dpGrid,0,sizeof(dpGrid));
for(i=0;i<9;i++)
{
for(j=0;j<9;j++)
{
if(board[i][j]!='.')
{
if(dpRow[i][board[i][j]-'0']==true)
return false;
if(dpCol[j][board[i][j]-'0']==true)
return false;
if(dpGrid[i/3*3+j/3][board[i][j]-'0']==true)
return false;
dpRow[i][board[i][j]-'0']=true;
dpCol[j][board[i][j]-'0']=true;
dpGrid[i/3*3+j/3][board[i][j]-'0']=true;
}
}
}
return true;
}
//搜索并填空
bool dfs(char **board,int boardRowSize,int boardColSize)
{
int i,j,k;
bool flag=false;
for(i=0;i<boardRowSize;i++)
{
for(j=0;j<boardColSize;j++)
{
if(board[i][j]=='.')
{
for(k=1;k<=9;k++)
{
board[i][j]=k+'0';
if(check(board,boardRowSize,boardColSize))
{
flag=dfs(board,boardRowSize,boardColSize);
if(flag==true)
return true;
}
board[i][j]='.';
}
return false;
}
}
}
return true;
}
int main()
{
int i;
board=(char **)malloc(9*sizeof(char *));
for(i=0;i<9;i++)
{
board[i]=(char *)malloc(9*sizeof(char));
}
for(i=0;i<9;i++)
{
scanf("%s",board[i]);
}
bool flag=dfs(board,9,9);
if(flag)
{
printf("求解成功:\n");
for(i=0;i<9;i++)
{
printf("%s\n",board[i]);
}
}
else
{
printf("无法求解!\n");
}
for(i=0;i<9;i++)
{
free(board[i]);
board[i]=NULL;
}
free(board);
board=NULL;
return 0;
}
作者:宋广泽,青岛某普通一本大学计算机专业在校生,本科在读,学生开发者。喜欢用C/C++编写有意思的程序,解决实际问题。
声明:本文为CSDN原创投稿,未经允许请勿转载。
人工智能学习路线+实战训练
https://edu.csdn.net/topic/ai30?utm_source=csdn_bw
【END】
作为码一代,想教码二代却无从下手:
听说少儿编程很火,可它有哪些好处呢?
孩子多大开始学习比较好呢?又该如何学习呢?
最新的编程教育政策又有哪些呢?
下面给大家介绍CSDN新成员:极客宝宝(ID:geek_baby)
戳他了解更多↓↓↓
热 文 推 荐
阿里云的物联网之路
☞一顿操作猛如虎!云原生应用为何如此优秀?
☞“踏实工作 7 年,辞职时老板头都不抬”
☞如何向 6 岁的孩子解释编程?这个解释厉害了
Google 究竟是不是要用 Fuchsia OS 取代 Android?
☞增长88%! 2019福布斯全球区块链50强榜单, 你未必看懂这3个细节
☞数据库不适合上容器云?| 技术头条
☞肖仰华:知识图谱落地,不止于“实现”
☞补偿100万?Oracle裁900+程序员,新方案已出!
点击阅读原文,输入关键词,即可搜索您想要的 CSDN 文章。
你点的每个“在看”,我都认真当成了喜欢
程序员休闲娱乐之数独!| 技术头条相关推荐
- 优秀的Java程序员应具备哪些编程技术?
想要成为一名合格的java程序猿,需要学习的知识是有很多的,但是基础知识一定要非常牢固,基础不牢固的程序员,随时都会被新的知识和技术所淘汰,下盘不稳风一吹就倒,那么具体作为一个优秀的Java程序员应具 ...
- 中级程序员教程-Cache映像技术
看了中级程序员有关Cache映像技术,总是迷迷糊糊的.我觉的这本叫"计算机组成原理"的书讲的很清楚 在Cache中用于存放数据或指令的镜头存储器称为内容Cache,用于存放数据或指 ...
- java技术栈有哪些_2020 年 Java 程序员应该学习掌握哪些技术?
原文:2020 年 Java 程序员应该学习掌握哪些技术? 作者:java技术剑 作为一名程序员,我们面临的最大挑战是使自己保持不断学习的状态.技术变化非常快,每两年你就会看到新版本的编程语言和框架. ...
- 程序员应该知道的国外技术网站
程序员应该知道的国外技术网站 国外学习网站: Codecademy是最受欢迎的免费编程学习网站之一: www.codecademy.com edX 领先的在线开源学习平台 www.edx.org 国外 ...
- 隆中对,程序员修炼之道,技术学习前进之路
之前写的 一个IT工薪族的4年奋斗成果 这篇文章,更多针对白领.互联网从业者.技术人员等广泛人群提出来的"职业发展路线",更准确的说法应该是"能力模型". 本 ...
- 程序员休闲好去处:深圳东湖公园和深圳仙湖植物园精美图片
深圳东湖公园和深圳仙湖植物园精美图片 原文:http://blog.sina.com.cn/s/blog_5e7c17950100gnnu.html 程序员休闲好去处:深圳东湖公园和深圳仙湖植物 ...
- 程序员如何提升个人的技术影响力
" 公司组织了个内训师培训班,进入前需要面试审核,以下是我的面试分享课题,这里分享出来以作记录,不出意外这应该是一个系列. " 大家晚上好,我是 howie6879,目前主要负责的 ...
- 我在经网的日子---从1个程序员开始建立的规范技术团队
讲述一段经历,总结一个从1个程序员开始建立的规范技术团队! 经网,一个立足于湖南的互联网公司. 2006年,以"湖南经济网"的名字进入网络新闻传媒界,2007年底,平均日IP20万 ...
- java程序员需要会前端吗_一个后端程序员,需要掌握前端技术吗?
一个后端程序员,需要掌握前端技术吗? JSP时代 8年前,刚刚进入编程这个行业,当时的Web开发使用古老的SSH框架+JSP.那个时候,几乎所有的Java程序员都要懂得如何写JavaScript.如何 ...
最新文章
- kmeans python interation flag_Python / Scipy Integration数组
- JavaScript 找出数组中重复的元素
- 计算机网络——HTTP协议和Web
- codova添加android慢_Android amp; iOS,请自动开始你们的 battle
- vmware虚拟机配置串口
- yolov4实现口罩佩戴检测,在验证集上做到了0.954的mAP
- [BZOJ4484][JSOI2015]最小表示(拓扑排序+bitset)
- 两个年月下拉列表html,html年月日下拉联动菜单 年月日三下拉框联动
- nodejs 之创建文件
- 免费的客户订单及商品管理系统
- BlackBerry7290上网步骤
- ECCV 2020预会议 直播笔记| Suppress and Balance: A Simple Gated Network for Salient Object Detection
- 零基础入门WordPress安装详细教程(图文)
- 计算机考研英语大纲,考研计算机大纲
- 1.出现了 page[pages/XXX/XXX] not found.May be caused by :1. Forgot to add page route in app.json.2. Inv
- 做网络必须掌握的83句话[转载]
- 计算机在生态学的应用,应用生态学
- 艺术家孙溟㠭艺术之路
- [练习][错误]MyBatis出错:Error instantiating class com.entity.Grade with invalid types () or values ().
- 网络富豪 百度李彦宏全球第二
热门文章
- 【区块链】以太坊truffle+web3+ganache简单实践
- oracle 建立一个游戏库,Power Designer怎么新建Oracle数据?建立Oracle数据教程分享
- 如何以源码安装mysql_CentOS以源码方式安装MySQL
- leetcode python3 简单题9. Palindrome Number
- python3 readlines的参数_Python3 File readlines() 方法
- atlas 200 远程图形化桌面
- Flutter代码锦囊---根据环境选择URL地址
- 生于俄罗斯的 Web 服务器王者 Nginx,现宣布俄罗斯禁止贡献
- 罗永浩回应“调侃”俞敏洪转行做直播;苹果3月9日举行春季发布会;CentOS推出新车载Linux发行版 | 极客头条...
- 谁说“IT 不理解 OT”?开放自动化来破局!