C++数独求解器与生成器

前几天笔者外出培训,刚刚学习了深度优先搜索,突然想到了数独的求解其实也可以用深搜实现,遂写了数独求解器与生成器。

1 数独求解器

1.1 预备

一开始,当然是头文件~

#include <iostream>
#include <stdlib.h>


接下来,是变量定义区

int sudoku[10][10];
//题目存储区
int row[10][10],column[10][10],block[4][4][10];
/*标记行、列、九宫格内某数字是否被使用过,例如row[4][7]=1代表第4行已有数字7*/
int known[10][10];
/*标记某单元格是否是题目所给数字,1代表题目中的数字*/
/*以上数组下标均从1开始使用*/

1.2 核心算法——深搜

相信学过深搜的朋友应该知道,深搜的过程含搜索与回溯两步。

void search(int x,int y){
	if (known[x][y]){
		search((9*x+y-9)/9+1,y%9+1);  //如果是题目中的数字则直接搜索下一个单元格,这里一并处理了y=9即现在所处单元格在行末的情况
	}
	else{
		if (x==10 && y==1) { //已经填完所有的数并合法
			print(); //打印输出
			exit(0); //找到一个解就退出。若欲找出所有的解,将此行去掉即可
		}
		else
		for (int i=1;i<=9;i++){  //枚举填数1~9
			if (row[x][i]==0 && column[y][i]==0 && block[(x-1)/3+1][(y-1)/3+1][i]==0){ //判断此数是否合法。九宫格计算略显复杂
				sudoku[x][y]=i;
				row[x][i]=1;
				column[y][i]=1;
				block[(x-1)/3+1][(y-1)/3+1][i]=1;
				search((9*x+y-9)/9+1,y%9+1);//填入下一个数,计算方法同第三行
				sudoku[x][y]=0;
				row[x][i]=0;
				column[y][i]=0;
				block[(x-1)/3+1][(y-1)/3+1][i]=0;//回溯:恢复之前的状态
			}
		}
	}
}


以上代码,一气呵成。

1.3 输出

为获得较好的视觉效果,笔者决定,输出格式用“-”和“|”画出单元格边界,用“=”和“||”表示九宫格边界和题目的边界。具体效果如下图所示:


简单。马上给出代码实现:

void print(){
	cout<<"    1   2   3    4   5   6    7   8   9   "<<endl;//列号
	cout<<"   =====================================  "<<endl;
	for (int i=1;i<=9;i++){
		cout<<i<<"|| ";//行号
		for (int j=1;j<=9;j++){
			if (j%3!=0) cout<<sudoku[i][j]<<" | ";//非九宫格边界
			else cout<<sudoku[i][j]<<" || ";//九宫格边界
		}
		cout<<endl;
		if (i%3!=0)cout<<"   -------------------------------------  "<<endl;//非九宫格边界
		else       cout<<"   =====================================  "<<endl;//九宫格边界
	}
}

当然笔者并未满足,整天对着黑白的控制台也会疯掉的。百度了一下C++如何使输入输出带上颜色之后,笔者改进了输出函数:

数独算法说明:用三个二维数组记录数独每个点的状态,SD(i, j)显示数值,也是真实数值(1到9)。ST(i, j)状态,1可由用户输入,2是题目给定的,不能改。SY(i, j)这符串,记录每个点中可能的值。 1、在进行自动计算时,只计算ST(i, j)为1的点,首先将所有状态为1的点的SY(i, j)值全部设为"123456789",SD(i, j)值全部设为0 2、逐点扫描,找到一个点,然后将该点所在的行、列、区域中已存在的SD(x, y)值从SY(i, j)中删除,因为数独规则是一个数值,在行、列、区域都不重复。 3、经第二步处理后,SY(i, j)为空,说明题目错误,SY(i, j)值为一位数字,就说明该点的值是唯一的,可以确定了。 4、剩余的SY(i, j)值最少也是二个数字的,或更多位数。随机从这些两位数的SY(i, j)中选取一个点。取其中的一位确定为该点的值后,重复第2步。如果错误遇错,则重复执行第4步。直到所有点都被确定。 注意:第2步是需要多次重复执行的,所有可用递归函数完成。如果执行结果出现错误(某数出现重复,或某点无值),需要对该过程所执行的所有操作进行回退。 第4步也是需要重复执行的。本和序用Goto跳转方式实现多次执行。 简单的数独,要么所有的点都具有独一值,第1步执行完成后,就已全部完成。或者具有多个解,随意猜测一个二位数的SY(i, j)的值都能成功。 难的数独,是可唯一确定的点很少,大部分点都有两种或多种可能的值,但最终正确答案只有一种或很少种解。 软件在自动计算过程中,具有很大的偶然性,对于骨灰级的数独题目在计算过程中,时间短的可能不到1秒就能完成,长的可能要几分钟,需要将各种可能性都测试一遍才有结果。 只要题目正确,多计算几次就能得到答案。 程序只处理有两种可能值的情况,对只存在三种可能值的情况未进一步处理,该情况非常极端了。 软件中包含网上下载的200个数独题目。
数独是一种经典的逻辑推理和填数字的游戏,其求解过程可以通过编写代码来实现。下面是一个简单的数独求解C代码的描述。 数独求解的主要思路是通过回溯法来逐个填充数独格子,然后检查填充的数字是否满足数独规则。如果填写的数字使数独有效,继续递归地填写下一个格子;如果填写的数字导致数独无效,则回溯到上一个格子重新尝试其他的数字。 首先,需要定义一个函数来判断当前填充的数字是否符合数独规则。这个函数需要检查当前数字是否和同一行、同一列以及同一个九宫格内的其他数字冲突。如果冲突,则说明当前数字无效。 然后,编写递归函数来填充数独格子。递归函数需要遍历数独的每一个格子,如果当前格子已经填充了数字,则跳过;如果当前格子未填充数字,则尝试从1到9逐个填充数字,并调用前面定义的函数来判断数字是否有效。如果数字有效,则递归地填充下一个格子;如果数字无效,则回溯到上一个格子重新尝试其他的数字。 最后,调用递归函数来填充数独格子。可以使用一个二维数组来表示数独,并将初始的数独状态作为参数传入递归函数。在递归函数结束后,如果数独全部填充完毕,则表明数独有解,否则无解。 这样,一个简单的数独求解C代码就完成了。通过递归和回溯的方式,可以在较短的时间内找到数独的解,并打印出来。当然,为了提高求解效率,可以在代码中加入一些优化算法,如剪枝等。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值