1. 简介
1.1 规则介绍
九宫格的定义:
水平方向的每一横行有九格,每一横行称为行(Row)
垂直方向的每一纵列有九格,每一纵列称为列(Column)
三行与三列相交之处有九格,每一单元称为小九宫(Box、Block)
所以,数独游戏的定义为:
数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。
1.2 解法介绍
数独的解法也体现了他的魅力:
- 直观法:唯一解法、基础摒除法、区块摒除法、唯余解法、矩形摒除法、单元摒除法、余数测试法;
- 候选数法:解数独题目需先建立候选数列表,根据各种条件,逐步安全的清除每个宫格候选数的不可能取值的候选数,从而达到解题的目的。
2 程序思路
如果借助计算机求解,我们先尝试使用基础的摒除法求解,即枚举各个可能的数进行试错,也称为“暴力破解”。
2.1 数据的存储表示
使用一维数组表示数独的题目,如这一串数字就能表示上节的数独的题目:
412560879086472500053001024075249386029605140364718290530900760008127450247056918
在程序中处理,二维数组使用起来更加方便,所以得解决:一维数组存储、二维数组转换的问题。使用 char (*)[9]
进行类型转换完成。
#define SIZE_BLK 3
#define SIZE_NUM (SIZE_BLK * SIZE_BLK)
#define ISDIGIT(n) ((n) >= '1' && (n) <= '9')
2.2 数独检查
数独检查需要判断行、列、宫是否存在重复数字,我们可以使用 int [9]
进行统计,更节省空间,考虑使用位图的形式进行标记:
#define bit_set(b, n) ((b) |= (0x1 << n))
#define bit_clr(b, n) ((b) &= ~(0x1 << n))
#define bit_chk(b, n) (((b) >> n) & 0x1)
struct instance
{
unsigned times;
unsigned bits_lines[SIZE_NUM];
unsigned bits_colum[SIZE_NUM];
unsigned bits_block[SIZE_NUM];
};
查看第一步载入数独题目,检查数据合规性,将数据保存到pdata二维数组中:
int sudoku_load(struct instance *pinst, char pdata[SIZE_NUM][SIZE_NUM])
{
for (int ix = 0; ix < SIZE_NUM; ix++) {
for (int jx = 0; jx < SIZE_NUM; jx++) {
if (ISDIGIT(pdata[ix][jx])) {
int nx = pdata[ix][jx] - '0';
int cx = ix / SIZE_BLK * SIZE_BLK + jx / SIZE_BLK;
if (bit_chk(pinst