Time Limit: 10000MS | Memory Limit: 65536K | |
Total Submissions: 4628 | Accepted: 2237 |
Description
![](http://poj.org/images%5C3076_1.jpg)
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;
}