简介
本程序先通过迭代推算出部分数字,然后通过DFS搜索解空间,迭代时排除了一部分不可能解以加速遍历
流程图
部分代码解析
全局变量
//sodu矩阵用于接收sudo谜题,且一切求解操作都作用于此
char sodu[9][9];
//possibility[x][y][z]代表了sodu矩阵中第x行,第y列,z是否可能存在
bool possibility[9][9][9];
//哈希表,在迭代完成后,标记所有空白,用于在遍历解空间时可以根据遍历深度快速找到目标空白
char hash[82][2];
函数功能描述
/*从文件读取sudo矩阵,并且初始化possibility矩阵*/
void EnterAndInitial()
/*在sudo数组中,各行各列各九宫格中,根据possibility矩阵寻找可以唯一确定的空白格*/
void GetUniquity()
/*用于维护possibility矩阵*/
void DiminishPossibility()
/*根据possibility矩阵,重新填充sodu矩阵,并且判断本次迭代是否有新的产出及是否求解完毕*/
int NextState()
/*迭代,并且判断迭代是否可以完成求解*/
bool IsCalculatable()
/*用于判断某矩阵是否违背sodu规则*/
bool IsRational()
/*深度优先搜索,并判断是否存在解*/
bool DFS(char depth)
迭代
bool IsCalculatable(){
int ptr = 0;
char rest = 81, lastStepRest = 81;
//如果迭代至没有空白块,则本sudo谜题已解决
while(rest>0){
//z389x56y4,若y不可能为7,x不可能为7,则z=7
GetUniquity();
//z389x56y4,可知xyz都不可能为3
DiminishPossibility();
//找到只有一个可能的单元格,填充,计算剩下几个空白块
rest = NextState();
//如果上一次剩余空白块和本次迭代剩余空白块数量相等,则继续迭代无效,需要搜索解空间
if(lastStepRest != rest) lastStepRest = rest;
else{
//在进行DFS前,先将所有空白块标记进入hash表
for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++) if(!sodu[cnt1][cnt2]){hash[ptr][0]=cnt1; hash[ptr++][1]=cnt2;}
hash[ptr][0] = -1; hash[ptr][1] = -1;
return false;
}
}
return true;
}
DFS
bool DFS(char depth){
//递归基,达到递归基则已找到解,因为在迭代完成后,已经把哈希表的结尾用-1标记
if(hash[depth][0] == -1) return true;
else{
for(int cnt1=1; cnt1<=9; cnt1++){
//如果cnt1在当前空白块可能存在(根据possibility[9][9][9]判断)
if(possibility[hash[depth][0]][hash[depth][1]][cnt1-1]){
sodu[hash[depth][0]][hash[depth][1]] = cnt1;
//假设此空白块为cnt1,若不违背数独规则
//则继续depth+1
if(IsRational()&&DFS(depth+1)) return true;
//以上任一条件不满足,则此块再次置空
else sodu[hash[depth][0]][hash[depth][1]] = 0;
}
}
return false;
}
}
全部代码
#include <iostream>
#include <fstream>
using namespace std;
char sodu[9][9];
bool possibility[9][9][9];
char hash[82][2];
void EnterAndInitial(){
ifstream input("sodu.txt");
for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++){
input >> sodu[cnt1][cnt2];
sodu[cnt1][cnt2] -= '0';
if(sodu[cnt1][cnt2]){
for(int cnt3=0; cnt3<9; cnt3++) possibility[cnt1][cnt2][cnt3] = false;
possibility[cnt1][cnt2][sodu[cnt1][cnt2]-1] = true;
}
else
for(int cnt3=0; cnt3<9; cnt3++) possibility[cnt1][cnt2][cnt3] = true;
}
}
void GetUniquity(){
int temp1, temp2, temp3, x, y;
for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++){
temp1 = 0;
for(int cnt3=0; cnt3<9; cnt3++) if(possibility[cnt1][cnt3][cnt2]){temp1++; temp2=cnt3;}
if(temp1 == 1){
sodu[cnt1][temp2] = cnt2+1;
for(int cnt3=0; cnt3<9; cnt3++) possibility[cnt1][temp2][cnt3] = false;
possibility[cnt1][temp2][cnt2] = true;
}
}
for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++){
temp1 = 0;
for(int cnt3=0; cnt3<9; cnt3++) if(possibility[cnt3][cnt1][cnt2]){temp1++; temp2=cnt3;}
if(temp1 == 1){
sodu[temp2][cnt1] = cnt2+1;
for(int cnt3=0; cnt3<9; cnt3++) possibility[temp2][cnt1][cnt3] = false;
possibility[temp2][cnt1][cnt2] = true;
}
}
for(x=0; x<9; x+=3) for(y=0; y<9; y+=3){
for(int cnt1=0; cnt1<9; cnt1++){
temp1 = 0;
for(int cnt2=0; cnt2<3; cnt2++) for(int cnt3=0; cnt3<3; cnt3++) if(possibility[cnt2+y][cnt3+x][cnt1]){temp1++; temp2=cnt2; temp3=cnt3;}
if(temp1 == 1){
sodu[temp2+y][temp3+x] = cnt1+1;
for(int cnt3=0; cnt3<9; cnt3++) possibility[temp2+y][temp3+x][cnt3] = false;
possibility[temp2+y][temp3+x][cnt1] = true;
}
}
}
}
void DiminishPossibility(){
int x, y;
for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++){
if(sodu[cnt1][cnt2]){
if(cnt1<3) y=0; else if(cnt1 < 6) y=3; else y=6;
if(cnt2<3) x=0; else if(cnt2 < 6) x=3; else x=6;
for(int cnt3=0; cnt3<9; cnt3++){
possibility[cnt1][cnt3][sodu[cnt1][cnt2]-1] = false;
possibility[cnt3][cnt2][sodu[cnt1][cnt2]-1] = false;
}
for(int cnt3=0; cnt3<3; cnt3++) for(int cnt4=0; cnt4<3; cnt4++) possibility[cnt3+y][cnt4+x][sodu[cnt1][cnt2]-1] = false;
}
}
}
int NextState(){
int amountOfPossibility;
int temp1;
int rest = 81;
for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++){
amountOfPossibility = 0;
for(int cnt3=0; cnt3<9; cnt3++){
if(possibility[cnt1][cnt2][cnt3]){
amountOfPossibility++;
temp1 = cnt3+1;
}
}
if(amountOfPossibility == 1) sodu[cnt1][cnt2] = temp1;
if(sodu[cnt1][cnt2]) rest--;
}
return rest;
}
bool IsCalculatable(){
int ptr = 0;
char rest = 81, lastStepRest = 81;
while(rest>0){
GetUniquity();
DiminishPossibility();
rest = NextState();
if(lastStepRest != rest) lastStepRest = rest;
else{
for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=0; cnt2<9; cnt2++) if(!sodu[cnt1][cnt2]){hash[ptr][0]=cnt1; hash[ptr++][1]=cnt2;}
hash[ptr][0] = -1; hash[ptr][1] = -1;
return false;
}
}
return true;
}
bool IsRational(){
int temp, x, y;
for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=1; cnt2<=9; cnt2++) for(int cnt3=0, temp=0; cnt3<9; cnt3++){
if(sodu[cnt1][cnt3] == cnt2) temp++;
if (temp > 1) return false;
}
for(int cnt1=0; cnt1<9; cnt1++) for(int cnt2=1; cnt2<=9; cnt2++) for(int cnt3=0, temp=0; cnt3<9; cnt3++){
if(sodu[cnt3][cnt1] == cnt2) temp++;
if (temp > 1) return false;
}
for(x=0; x<9; x+=3) for(y=0; y<9; y+=3)
for(int cnt1=1; cnt1<=9; cnt1++){
temp=0;
for(int cnt2=0; cnt2<3; cnt2++) for(int cnt3=0; cnt3<3; cnt3++){
if(sodu[cnt2+x][cnt3+y] == cnt1) temp++;
if(temp > 1) return false;
}
}
return true;
}
bool DFS(char depth){
if(hash[depth][0] == -1) return true;
else{
for(int cnt1=1; cnt1<=9; cnt1++){
if(possibility[hash[depth][0]][hash[depth][1]][cnt1-1]){
sodu[hash[depth][0]][hash[depth][1]] = cnt1;
if(IsRational()&&DFS(depth+1)) return true;
else sodu[hash[depth][0]][hash[depth][1]] = 0;
}
}
return false;
}
}
bool Solve(){
if(IsRational())
if(IsCalculatable()||DFS(0)) return true; else return false;
else return false;
}
void Output(bool solvable){
if(solvable){
for(int cnt1=0; cnt1<9; cnt1++){
for(int cnt2=0; cnt2<9; cnt2++){
sodu[cnt1][cnt2] += '0';
cout << sodu[cnt1][cnt2] << ' ';
}
cout << "\n";
}
}
else cout << "unsolvable!";
}
int main(){
EnterAndInitial();
Output(Solve());
return 0;
}
测试样例
8 0 0 0 0 0 0 0 0
0 0 3 6 0 0 0 0 0
0 7 0 0 9 0 2 0 0
0 5 0 0 0 7 0 0 0
0 0 0 0 4 5 7 0 0
0 0 0 1 0 0 0 3 0
0 0 1 0 0 0 0 6 8
0 0 8 5 0 0 0 1 0
0 9 0 0 0 0 4 0 0
本题目来源于号称“世界上迄今难度最大的数独游戏”
CPU Intel i5-7300HQ
Memory 8.00GB
System Windows 10 Professional
改进余地
1.本人对数独技巧不很精通,通过迭代能找出的空白较少,使用更高级的技巧进行迭代则可以进一步缩小DFS的解空间
2.程序只能找出一个解(存在很多解的数独谜题是存在的),因为本程序一旦找到一个解就不继续寻找了