数独问题的一种简单算法代码实现

五一期间无聊时想起去年考研复试有一道上机题目当时没作出来,于是一时兴起想重新拾起看看是当时太紧张,还是自己能力不足。然后发现这道题目还真稍微有些难度,相当于一道数独问题(sudoku)的简化版。自己想来想去也只能想到两种算法,一种是拿剩余元素做全排列测试,一种是回溯法测试。最后只实现了一个全排列测试的算法。然后又发现自己要写一个非递归的全排列(permutation)也有难度,想了两天,也没搞出来,自我BS了一把,感觉这么基本的算法都搞不定,还想参加参加ACM/ICPC什么的,真是不自量力。后来还是挑战自我思考失败,只能上网查了一下,发现组合数学(combinatorics)研究了这个东西,给出很多算法,照那些算法实现的话相当容易。

 

最后搞出来还是有一点成就感,代码贴出来:

/* HUST_IPRAI:2008考研复试题 有一个4×4的方阵。要求:每个元素必须是1,2,3,4其中的一个,而且每一行不能重 复,例如不能有2个1等,每一列也不能重复。而且将矩阵分成4个2×2的矩阵,每个小方阵 也不能有重复。 (1),给出方阵,如 1 4 2 3 2 3 4 1 4 1 3 2 3 2 1 4 编一程序,判断此矩阵是否满足要求 (2) 有一矩阵 3 ? ? ? ? 2 ? ? ? 4 1 ? ? ? 3 ? 编一程序,求出矩阵其他的元素。 author: MulinB date: 2009-05-01 */ #include #include using namespace std; #define MATRIX_WIDTH 4 //矩阵阶数 #define SMALL_CELL_WIDTH 2 //小单元格 //打印矩阵 void DisplayMatrix(int matrix[MATRIX_WIDTH][MATRIX_WIDTH]) { cout << "-------------matrix--------------" << endl; for (int i=0; i MATRIX_WIDTH) return false; //检查行里有无重复 for (k=0; k 1 && matrix[i][j] < MATRIX_WIDTH) { numCounter[matrix[i][j]-1]++; //该数字的counter加1 continue; //已经填上 } } } //统计待全排列的数字,比如:如果2出现了1次,那么还有MATRIX_WIDTH-1个2需要排列 int elems[MATRIX_WIDTH*MATRIX_WIDTH] = {0}; //待全排列的数字的容器 int numOfElem = 0; for (int m=0; mpj}(右边的数从右至左是递增的,因此k是所有大于pj的数字中序号最大者) 3)对换pj,pk 4)再将pj+1......pk-1pkpk+1pn倒转得到排列p''=p1p2.....pj-1pjpn.....pk+1pkpk-1.....pj+1,这就是排列p的下一个下一个排列。 例如839647521是数字1~9的一个排列。从它生成下一个排列的步骤如下: 自右至左找出排列中第一个比右边数字小的数字4 839647521 在该数字后的数字中找出比4大的数中最小的一个5 839647521 将5与4交换 839657421 将7421倒转 839651247 所以839647521的下一个排列是839651247。 */ //全排列算法:这里使用字典序算法 int elemNewPerm[MATRIX_WIDTH*MATRIX_WIDTH] = {0}; //用来存放每个排列的元素 memcpy(elemNewPerm, elems, sizeof(int)*numOfElem); //初始化为初始序列 int pos_j = 0; int pos_k = 0; int p_k = 0; while (true) { //----------------------------------------- //测试当前排列填入矩阵是否合法 int idxToBeFilled = 0; for (int i=0; i 1 && matrix[i][j] < MATRIX_WIDTH) { tempMatrix[i][j] = matrix[i][j]; //拷贝原来元素 } else { tempMatrix[i][j] = elemNewPerm[idxToBeFilled++];//填入待填入的元素 } } } if (VerifyMatrix(tempMatrix) == true) //测试是否符合要求 { DisplayMatrix(tempMatrix); //符合条件,打印出来 cout << "The above matrix is an OK answer!" << endl; } //------------------------------------------- //------------------------------------------- //全排列字典序列算法 //从右侧寻找第一个比右侧小的元素j for (pos_j=numOfElem-2; pos_j>=0; pos_j--) { if (elemNewPerm[pos_j] < elemNewPerm[pos_j+1]) break; //找到j } if (pos_j < 0) break; //没有找到j //寻找j右侧比pj大的最小数 p_k = elems[numOfElem-1]; //先令p_k为最大值 pos_k = 0; for (int x=pos_j; x elemNewPerm[pos_j] && elemNewPerm[x] <= p_k) //找比pj大的最小值 { p_k = elemNewPerm[x]; pos_k = x; //找到k } } if (pos_k == 0) break; //没有找到k //调换pj和pk SwapValue(elemNewPerm[pos_j], elemNewPerm[pos_k]); //翻转j之后的序列,不包括j for (int y=1; y<=(numOfElem-1-pos_j)/2; y++) { SwapValue(elemNewPerm[pos_j+y], elemNewPerm[numOfElem-y]); } } //TODO: 算法2:使用栈进行回溯 return true; } int main() { int MatrixToBeFilled[MATRIX_WIDTH][MATRIX_WIDTH] = {{3, 0, 0, 0}, {0, 2, 0, 0}, {0, 4, 1, 0}, {0, 0, 3, 0}}; FillMatrix(MatrixToBeFilled); //解题 return 0; }

 

运行结果如下:

-------------matrix-------------- 3 1 2 4 4 2 1 3 1 3 4 2 2 4 3 1 --------------------------------- The above matrix is an OK answer! -------------matrix-------------- 3 1 2 4 4 2 1 3 2 3 4 1 1 4 3 2 --------------------------------- The above matrix is an OK answer! -------------matrix-------------- 3 1 4 2 4 2 1 3 1 3 2 4 2 4 3 1 --------------------------------- The above matrix is an OK answer! -------------matrix-------------- 3 4 1 2 1 2 4 3 4 3 2 1 2 1 3 4 --------------------------------- The above matrix is an OK answer! -------------matrix-------------- 3 4 2 1 1 2 4 3 2 3 1 4 4 1 3 2 --------------------------------- The above matrix is an OK answer! -------------matrix-------------- 3 4 2 1 1 2 4 3 4 3 1 2 2 1 3 4 --------------------------------- The above matrix is an OK answer!

 

把宏改成9的话,应该可以解决九宫格数独问题,懒得试,主要是怕不行,没时间去改……

算法比较慢,有待改进,可惜大量时间要花在老板的项目和自己的GRE/TOEFL上,所以没时间细扣,唉,有悖程序员精益求精、锲而不舍的精神啊,BS一下懒惰的自己……

 

你可能感兴趣的:(数独问题的一种简单算法代码实现)