数独是根据 9×9 盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫内的数字均含 1−9 ,不重复。每一道合格的数独谜题都有且仅有唯一答案,推理方法也以此为基础,任何无解或多解的题目都是不合格的。
这道题是一道经典的搜索与回溯问题,由于数独时空复杂度有限,所以搜索与回溯也可以解决问题,我们从数独的性质入手,满足每一行、每一列、每一个粗线宫内的数字均含 1−9 ,不重复,那么我们需要三个标记数组,分别标记当前行,列,小九宫格内是否有某个数字,那么我们怎么确定当前点是哪一个小九宫格呢,对于,(x,y)点来说我们假设他是第id个小九宫格,那么/*
id:(x,y)在哪个九宫格
id = (x-1)/3*3+(y-1)/3+1
(x-1)/3:确定当前点在哪一个大行
(y-1)/3:确定当前点在哪一个大列
*3:因为一个大行有3个小列
+1:九宫格从1开始
*/
接下来我们只需要,深度搜索加回溯即可解决问题,深度搜索分为6步,
1.枚举所有方案:在(x,y)格枚举所有数字
2.判断标记:判断是否符合数独规则
3.搜索加标记:填数,标记
4.进入下一层:搜索下一个格子
5.回溯:如果当前数字不符合规则,则要回溯到上一层,并清空标记
6.终止条件:如果行遍历到第九层,结束,如果遍历到当前层最后一列,则遍历下一层,如果当前层有数字,则遍历下一个小格子
#include <bits/stdc++.h>
using namespace std;
const int N = 1e2+10;
#define int long long
bool vis_r[N][N];
bool vis_c[N][N];
bool vis_id[N][N];
int a[N][N];
int n = 9;
/*
id:(x,y)在哪个九宫格
id = (x-1)/3*3+(y-1)/3+1
(x-1)/3:确定当前点在哪一个大行
(y-1)/3:确定当前点在哪一个大列
*3:因为一个大行有3个小列
+1:九宫格从1开始
*/
bool dfs(int x, int y) {
//6.终止条件
if (x == n + 1) return true;
if (y == n + 1) return dfs(x+1,1);
if (a[x][y] != 0) return dfs(x,y+1);
//1.在每个格子枚举所有数字
for (int c = 1; c <= n; c++) {
//2.判断标记
int id = (x - 1) / 3 * 3 + (y - 1) / 3 + 1;
if (/*!vis_r[x][c] && !vis_c[y][c] && !vis_id[id][c]*/!vis_r[c][x]&&!vis_c[c][y]&&!vis_id[c][id]) {
//3.搜索+标记
a[x][y] = c;
/*vis_r[x][c] = vis_c[y][c] = vis_id[id][c] = 1;*/
vis_r[c][x] = vis_c[c][y] = vis_id[c][id] = 1;
//4.下一层(下一个格子)
if (dfs(x, y + 1) == true) return true;
//5.回溯
a[x][y] = 0;
/*vis_r[x][c] = vis_c[y][c] = vis_id[id][c] = 0;*/
vis_r[c][x] = vis_c[c][y] = vis_id[c][id] = 0;
}
}
return false;
}
signed main() {
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cin >> a[i][j];
if (a[i][j] != 0) {//该位置本身就有数字
int x = a[i][j], id = (i - 1) / 3 * 3 + (j - 1) / 3 + 1;
/*vis_r[i][x] = vis_c[j][x] = vis_id[id][x] = 1;*/
//vis_r[x][i] = vis_c[x][j] = vis_id[x][id] = 1 也可以
//前者标记数组记录的是哪一行,哪一列,哪一小宫格x是否存在,后者标记x在哪一行,哪一列,哪一小宫格被标记
vis_r[x][i] = vis_c[x][j] = vis_id[x][id] = 1;
}
}
}
dfs(1, 1);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cout << a[i][j] << " ";
}cout << endl;
}
return 0;
}