一直想写个程序解数独问题但是一直没有时间,趁这两天放寒假了终于可以来写一写程序了。程序的运行结果还是比较理想的,Ubuntu里面“Very hard”级别被秒杀。所谓的四分钟其实我有两分钟在输入,两分钟在输出什么的……这里陈述一下主要思想,希望能与高手交流~~完整的代码我上传在我的资源里面了。
要解决数独问题,想来想去,只能穷举了。但是我肯定不能用简单的穷举,要是完全穷举的话估计是个阶乘级的复杂度,这种程序写出来也是没有意义的,必须想一个好一点的办法。
首先我需要确定一些数据结构。肯定我们这里需要一个数组,9*9的,来存放数独的表格(当然我实际选用的是一个含81个元素的一维数组);除此之外,考虑到要经常查询某个元素是否可以放置在某个位置上,我们还需要一个“集合”。这里我选用的是位图集合,因为有我以前写好的现成的函数可以用。我总共用了27个集合,分别表示9行、9列和9个九宫格。Main函数的伪代码框架如下所示:
int main(void) {
struct my_bitmap set[27];
int i, j, cells[81], *array;
//自己定义的位图集合
//0-8 for rows 9-17 for columns 18-26 for cells
init();
//读初始化文件,初始化数组、集合
array= my_try();
//递归的解问题
display();
return 0;
}
那么要如何解决问题呢?我采用的是简单的贪心算法,每次找当前含有最多信息的未被填满的那个“单元”(这里所谓的“单元”指的是一行或者一列或者一个九宫格),从这个单元里面找出一个未被被填充的空格,从1-9依次尝试,如果i可以被填入这个空格,那么就将其填入,如果不能就换i+1尝试。这时我们就可以递归的解决这个新产生的问题了。当只剩下一行或一列未被填满时,可以算是Basecase了,因为对于这一行的每一列(或者这一列的每一行),都只有一个元素未被填入,这个元素必然是唯一的,可以直接解出来。于是我们可以写出这里递归的主体框架了:
int* my_try(int* array) {
int max_notfull, i, r, c, flag, pos;
int *ret;
max_notfull = get_max_notFull();
//找出尚未被填满的“单元”中含有最多信息的单元
if (/*trival case*/)
//如果是前面所说的“basecase”
return trival_solution();
pos = get_nextIdx(max_notfull, array,);
//从当前单元中获取第一个未被填充的空格位置
if (pos == -1)
return array;
//表示已全部填满
r = pos / 9;
c = pos % 9;
for (i = 1; i < 10; i++) {
if (mark(r, c, i, array) != -1) {
//尝试将i放入第r行第c列这个位置,返回-1表示放入
//不成功,存在冲突
ret = my_try(array);
//递归解决问题
if (ret)
/*ret != 0 说明成功解决问题
返回0 说明这种尝试不可能成功
*/
return ret;
erase(r, c, array);
//从数组中删除这次添加值,尝试下一次
}
}
//全部尝试完,说明无解,返回0
return 0;
}
这里面有一些函数我就不说明他们的具体实现了,函数的含义在注释中都说明了,这里帖一下头文件 function.h
#ifndef FUNCTION_H_
#define FUNCTION_H_
#include "bitmap.h"
void init(int* array, struct my_bitmap* sets);
int get_max_notFull(struct my_bitmap* sets);
//Return index of the unit which is not full and contains the
//most information. 0-8 for rows, 9-17 for colunms and 18-26 for cells.
//One bit among 16-18 is marked, to indicate that it is the last unit
//which is not full.
int mark(int row, int colunm, int value, int* array, struct my_bitmap* sets);
//put value in the position row*colunm, return value
//or -1 if fail(already occupied or invalid parameter).
int erase(int row, int colunm, int* array, struct my_bitmap* sets);
//erase the remark in the particular postion; return 0
//or return -1 if fail(not occupied)
int get_rowset_index(int row, int colunm);
//return 0-8, depends on row
int get_colset_index(int row, int colunm);
//return 9-17, depends on colunm
int get_cellset_index(int row, int colunm);
//return 18-27
int get_nextIdx(int unit, int* array, struct my_bitmap* sets);
//Find the first index in this unit which is not occupied
//return -1 if not find;
int* my_try(int* array, struct my_bitmap* sets);
int* trival_solution(int unit, int flag, int* array, struct my_bitmap* sets);
#endif /* FUNCTION_H_ */