Time Limit: 10000MS | Memory Limit: 65536K | |
Total Submissions: 4628 | Accepted: 2237 |
Description
Write a Sudoku playing program that reads data sets from a text file.
Input
Output
Sample Input
--A----C-----O-I -J--A-B-P-CGF-H- --D--F-I-E----P- -G-EL-H----M-J-- ----E----C--G--- -I--K-GA-B---E-J D-GP--J-F----A-- -E---C-B--DP--O- E--F-M--D--L-K-A -C--------O-I-L- H-P-C--F-A--B--- ---G-OD---J----H K---J----H-A-P-L --B--P--E--K--A- -H--B--K--FI-C-- --F---C--D--H-N-
Sample Output
FPAHMJECNLBDKOGI OJMIANBDPKCGFLHE LNDKGFOIJEAHMBPC BGCELKHPOFIMAJDN MFHBELPOACKJGNID CILNKDGAHBMOPEFJ DOGPIHJMFNLECAKB JEKAFCNBGIDPLHOM EBOFPMIJDGHLNKCA NCJDHBAEKMOFIGLP HMPLCGKFIAENBDJO AKIGNODLBPJCEFMH KDEMJIFNCHGAOPBL GLBCDPMHEONKJIAF PHNOBALKMJFIDCEG IAFJOECGLDPBHMNK
这个数独的题目用到的是之前一篇文章中用到的DLX算法,即将数独问题转化为精确覆盖问题。具体转化的方法是根据数独的特性来的,首先作为数独,它的约束条件有4个
1.每一行每个数字只能填一遍
2.每一列每个数字只能填一遍
3.每个宫每个数字只能填一遍
4.每个格子只能填一个数字。
根据上面的规则,我们将数独问题转化为精确覆盖问题。矩阵构造如下:
第1列定义为第一行填了数字1
第2列定义为第一行填了数字2
第16列定义为第一行填了数字16
第17列定义为第二行填了数字1
以此类推,第256列定义为第16行填了数字16
这样,用1-256列完成了第一个约束条件,下面我们继续:
第257列定义为第1列填了数字1
第258列定义为第1列填了数字2
第512列定义为第16列填了数字16
这样,用257-512列完成了第二个约束条件
第513列定义为在第一宫填了数字1
第514列定义了在第一宫填了数字2
第768列定义了在第十六宫填了数字16
这样,513-768列完成了第三个约束条件
第769列定义了(1,1)填了一个数字
第770列定义了(1,2)填了一个数字
在1024列定义了(16,16)填了一个数字
这样,769-1024列完成了第四个约束条件
举个例子:
例如(6,4)这个点填了3,分别转化为矩阵中对应的列为:
1.在第6行中填了数字3,对应列数为:(6 - 1) * 16 + 3
2.在第4列中填了数字3,对应列数为:16 * 16 + (4 - 1) + 3
3.在第5宫中填了数字3,对应列数为:16 * 16 * 2 + (5 - 1) * 16 + 3 (其中宫的算法是:palace = (i - 1) /4 * 4 + (j - 1) / 4 + 1)
4.在(6,4)填了一个数字,对应列数为:16 * 16 * 3 + 84 (其中位置的算法是:pos = (i - 1) * 16 + j)
这样,对应(6,4)这个填3对应矩阵的一行就是:上述的列数置为1,其余列都是0,将这一列加入到矩阵中去。
至此所有约束条件完成了,矩阵构造完成了,实际上矩阵已然转化为精确覆盖问题,就可以使用之前转载的一篇博客中的DLX算法求解:http://blog.csdn.net/chilumanxi/article/details/48297149,具体见代码:
/************************************************************************* > File Name: Sudoku.cpp > Author: Zhanghaoran0 > Mail: chiluamnxi@gmail.com > Created Time: 2015年09月08日 星期二 20时52分43秒 ************************************************************************/ #include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; const int N = 5000; const int M = 700000; int U[M], D[M], L[M], R[M]; int C[M]; int H[N], S[N]; int mark[M]; int size, n, m, Suc[N], flag; int tnum[M]; void Link(int a, int b){ S[b] ++; //对应列数结点数+1 C[size] = b; //记录列数 U[size] = U[b]; //将上下的联系连接上 D[U[b]] = size; D[size] = b; U[b] = size; if(H[a] == -1) //如果该行还没有加入过结点,指向自己 H[a] = L[size] = R[size] = size; else{ L[size] = L[H[a]]; //若已经加入过结点,则将此结点加入,将左右的连接关系加上。 R[L[H[a]]] = size; R[size] = H[a]; L[H[a]] = size; } mark[size] = a; size ++; } void Delete(int a){ //删除结点 L[R[a]] = L[a]; R[L[a]] = R[a]; for(int i = D[a]; i != a; i = D[i]){ for(int j = R[i]; j != i; j = R[j]){ U[D[j]] = U[j]; D[U[j]] = D[j]; S[C[j]] --; } } } void Resume(int a){ for(int i = U[a]; i != a; i = U[i]){ //只是改变了周围四个指针的数值,所以很简单就能恢复 for(int j = L[i]; j != i; j = L[j]){ U[D[j]] = j; D[U[j]] = j; S[C[j]] ++; } } L[R[a]] = a; R[L[a]] = a; } void Dance(int a){ int Min = N; int t; if(!R[0]){ //如果作为辅助的第一行没有任何结点的话就视为完成 flag = 1; sort(Suc, Suc + a); for(int i = 0; i < a; i ++){ cout << (char)(tnum[mark[Suc[i]]] + 'A' - 1); if((i + 1) % 16 == 0) cout << endl; } cout << endl; return ; } for(int i = R[0]; i; i = R[i]){ //优先删除结点少的列数 if(S[i] < Min){ Min = S[i]; t = i; } } Delete(t); for(int i = D[t]; i != t; i = D[i]){ Suc[a] = i; //用这个数组来记录一行所有的字母 for(int j = R[i]; j != i; j = R[j]) Delete(C[j]); Dance(a + 1); if(flag) return ; for(int j = L[i]; j != i; j = L[j]) Resume(C[j]); } Resume(t); //回溯 } char Map[20][20]; int Matrix[16 * 16 * 16 + 10][16 * 16 * 4 + 10]; int main(void){ while(scanf("%s", Map[0]) == 1){ for(int i = 1; i < 16; i ++) scanf("%s", Map[i]); memset(Matrix, 0, sizeof(Matrix)); m = 16 * 16 * 4; n = 0; for(int i = 1; i <= 16; i ++){ for(int j = 1; j <= 16; j ++){ int row = i; int col = j; int palace = (i - 1) / 4 * 4 + (j - 1) / 4 + 1; //宫的位置 int pos = (i - 1) * 16 + j; //实际位置 if(Map[i - 1][j - 1] == '-'){ for(int k = 1; k <= 16; k ++){ //对于还没有存入数字的坐标,将所有情况写入 ++ n; tnum[n] = k; //记录存入数组的个数和对应的数字 Matrix[n][(row - 1) * 16 + k] = 1; //以下四个条件分别对应行,列,宫,实际位置的条件 Matrix[n][16 * 16 + (col - 1) * 16 + k] = 1; Matrix[n][16 * 16 * 2 + (palace - 1) * 16 + k] = 1; Matrix[n][16 * 16 * 3 + pos] = 1; } } else{ int k = Map[i - 1][j - 1] - 'A' + 1; //对于实际存在的数字,同上写入 ++ n; tnum[n] = k; Matrix[n][(row - 1) * 16 + k] = 1; Matrix[n][16 * 16 + (col - 1) * 16 + k] = 1; Matrix[n][16 * 16 * 2 + (palace - 1) * 16 + k] = 1; Matrix[n][16 * 16 * 3 + pos] = 1; } } } for(int i = 0; i <= m; i ++){ //初始化所有的结点的连接关系,上下的关系没有连接所以先指向自己 S[i] = 0; D[i] = U[i] = i; L[i + 1] = i; R[i] = i + 1; } R[m] = 0; //循环的性质 size = m + 1; memset(H, - 1, sizeof(H)); for(int i = 1; i <= n; i ++){ for(int j = 1; j <= m; j ++){ if(Matrix[i][j]) Link(i, j); //将所有已经存在的点连接 } } flag = 0; Dance(0); } return 0; }