数独是一个我们都非常熟悉的经典游戏,运用计算机我们可以很快地解开数独难题,现在有一些简单的数独题目,请编写一个程序求解。
如有多解,输出一个解
思路一 简单数独
通过找出空缺位置在行、列、3x3上的缺值,进行补充。
cpp
#include <iostream>
using namespace std;
int main() {
int blockNum = 0, whileTime = 200;
int shudu[9][9] = {0};
bool fullFlag = false;
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
cin >> shudu[i][j];
if (0 == shudu[i][j]) {
// isFull[i][j] = false;
blockNum++;
}
}
}
while (!fullFlag && whileTime) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
if (!shudu[i][j]) {
//缺少的为false,有的为true
bool numList[9] = {false};
int lackTime = 0;
int lackSet = -1;
/*
步骤一:找出当前位置所在行、列、块的缺值(numList)
*/
//行,行标不变遍历列
for (int j_ = 0; j_ < 9; j_++) {
if (shudu[i][j_]) {
numList[shudu[i][j_] - 1] = true;
}
}
//列,列标不变遍历行
for (int i_ = 0; i_ < 9; i_++) {
if (shudu[i_][j]) {
numList[shudu[i_][j] - 1] = true;
}
}
//3x3,遍历所在块
for (int i_ = (i / 3); i_ < (i / 3 + 1) * 3; i_++) {
for (int j_ = (j / 3); j_ < (j / 3 + 1) * 3; j_++) {
if (shudu[i_][j_]) {
numList[shudu[i_][j_] - 1] = true;
}
}
}
/*
步骤二:判断当前位置的缺失值是否唯一
*/
for (int k = 0; k < 9; k++) {
if (!numList[k]) {
lackTime++;
lackSet = k;
}
}
if (1 == lackTime) {
shudu[i][j] = lackSet + 1;
blockNum--;
whileTime++;
if (0 == blockNum) {
fullFlag = true;
}
}
}
}
}
whileTime--;
}
if (fullFlag) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
cout << shudu[i][j] << ' ';
}
cout << endl;
}
} else {
cout << "解题失败!";
}
return 0;
}
代码使用了多次循环,寄希望与可以通过多轮迭代完成最终解答,但是无法处理多候选数情况、循环次数也可能冗余或不足。此外应对多候选时需要使用回溯机制。
思路二
引入回溯机制,当发现状态空间树上该路径无法得到正确解时,通过逐层回溯进行下一次尝试直到得到最终解。
cpp
#include <iostream>
using namespace std;
bool isVilad(int MIX[9][9], int I, int J, int num) {
int i = (I / 3 ) * 3;
int j = (J / 3 ) * 3;
//检查行
for (int _j = 0; _j < 9; _j++) {
if (num == MIX[I][_j])
return false;
}
//检查列
for (int _i = 0; _i < 9; _i++) {
if (num == MIX[_i][J])
return false;
}
//检查3x3
for (int _i = i; _i < i + 3; _i++) {
for (int _j = j; _j < j + 3; _j++) {
if (num == MIX[_i][_j])
return false;
}
}
return true;
}
bool solve(int MIX[9][9]) {
for (int i = 0; i < 9; i++) {
for (int j = 0 ; j < 9; j++) {
//如果为空,则进行补充
if (0 == MIX[i][j]) {
for (int num = 1; num <= 9; num++) {
//尝试1~9
if (isVilad(MIX, i, j, num)) {
MIX[i][j] = num;
if (solve(MIX))
return true;
MIX[i][j] = 0;
}
}
return false;
}
}
}
return true;
}
int main() {
int board[9][9];
// 读取输入
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
cin >> board[i][j];
// 求解并输出
if (solve(board)) {
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
cout << board[i][j];
if (j < 8) cout << ' ';
}
cout << endl;
}
} else {
cout << "返回False" << endl;
}
return 0;
}
思路三:
将前两者结合,即在迭代时,只从当前位置空缺的选项中选择,能提高一定时间?
或许,但是可能直接回溯更快,因为重复项会在第一次判断时直接PASS,从而不会增加过多时间。