P1784 数独(搜索与回溯)

数独是根据 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值