以前也想过很久解数独的算法,但是没有得到很简单的方法,某天看到某位学长的代码,恍然大悟,本以为暴力搜索会很花时间(10^81种可能),没想到实际上由于各种限制,枚举次数竟然普遍小于10000次,用dfs便可实现每种可能都列举。这样计算一个数独就很快了(不到1ms),下面附上自己理解改动并加了注释的代码,DEV编译可能会效果好一些。
代码:
#include <stdio.h>
#include<stdlib.h>
#include<windows.h>
char map[9][9],xx;
int ans=0;
bool suit=false;
void position(int x,int y) {
COORD pos={x,y};
HANDLE Out=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(Out,pos);
}
void Hide() {
HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO CursorInfo;
GetConsoleCursorInfo(handle,&CursorInfo); //获取控制台光标信息
CursorInfo.bVisible = false; //隐藏控制台光标
SetConsoleCursorInfo(handle,&CursorInfo); //设置控制台光标状态
}
void print(int x,int y) {
ans++;
position(y*2,x);
printf("%d",map[x][y]);
position(0,11);
printf("\n已尝试:%d次\n",ans);
}
bool check(int n,int v) {
int x=n%9;
int y=n/9;
for(int i=0;i<9;i++) if(map[x][i]==v||map[i][y]==v) return false; //检验横竖
x=x/3*3;
y=y/3*3;
for(int i=x;i<x+3;i++) for(int j=y;j<y+3;j++) if(map[i][j]==v) return false; //检验九宫格
return true;
}
void dfs(int n) { //暴力查找
int x=n%9;
int y=n/9;
if (n==81||suit) { //出口
suit=true;
return;
}
if (map[x][y]!=0) dfs(n+1);
else for(int i=1;i<=9;i++) if(check(n,i)) {
map[x][y]=i;
print(x,y);
dfs(n+1);
if(suit) return;
map[x][y]=0;
}
}
int main() {
int i,j;
freopen("1.txt","r",stdin); //从文档读入
Hide(); //隐藏光标
for (i=0;i<9;i++) {
for (j=0;j<9;j++) {
scanf("%c",&map[i][j]);
map[i][j]-='0';
}
if(i!=8||j!=8) scanf("%c",&xx); //读换行
}
for(i=0;i<9;i++) { //初始化屏幕
for(j=0;j<9;j++) printf("%d ",map[i][j]);
printf("\n");
}
position(25,5);
printf("正在求解数独。。。");
dfs(0);
printf("\n求解完毕!\n");
}
效果图:
还可以再加一个求出所有解的动能。
________________________________________________________________分割线
2018.12.24更新,由于有人评论DFS并不快,不得不解释一下耗时的地方是printf语句,去掉之后1S之内完全可以解出来,之前只是为了让大家看清求解过程才去加了很多过程语句,下面是快速求解的代码:
#include <stdio.h>
#include<stdlib.h>
char map[9][9],xx;
int ans=0;
bool suit=false;
bool check(int n,int v) {
int x=n%9;
int y=n/9;
for(int i=0;i<9;i++)
if(map[x][i]==v||map[i][y]==v)
return false; //检验横竖
x=x/3*3;
y=y/3*3;
for(int i=x;i<x+3;i++)
for(int j=y;j<y+3;j++)
if(map[i][j]==v)
return false; //检验九宫格
return true;
}
void dfs(int n) { //暴力查找
int x=n%9;
int y=n/9;
if (n==81||suit) { //出口
suit=true;
return;
}
if (map[x][y]!=0)
dfs(n+1);
else
for(int i=1;i<=9;i++)
if(check(n,i)) {
map[x][y]=i;
dfs(n+1);
if(suit) return;
map[x][y]=0;
}
}
int main() {
int i,j;
freopen("1.txt","r",stdin); //从文档读入
for (i=0;i<9;i++) {
for (j=0;j<9;j++) {
scanf("%c",&map[i][j]);
map[i][j]-='0';
}
if(i!=8||j!=8)
scanf("%c",&xx); //读换行
}
dfs(0); //求解
for(int i=0;i<9;i++) { //输出答案
for(int j=0;j<9;j++) {
printf("%d ",map[i][j]);
}
printf("\n");
}
system("pause");
}