1317. Sudoku
解题技巧:1.数独每个格子记录自身还能填的数字,每次寻找能填数字数最少的格子填充数字。
2.使用“禁用计数”的方式,记录每个格子某个数字被禁用的次数,以便脱离禁用后恢复可填性。
#include <cstdio>
#include <cstring>
#include <set>
#include <vector>
using std::set;
using std::vector;
struct Elem {
int num;
set<int> lefts;
};
set<int> LEFTS;
const int maxn = 9;
char charMap[maxn][maxn+1];
Elem Map[maxn][maxn];
int ans[maxn][maxn];
int count;
int f[maxn][maxn][maxn + 1];
struct Cor {
int r, c;
Cor(int r_ = 0, int c_ = 0) : r(r_), c(c_) {}
bool operator<(const Cor &cor) const {
return (r < cor.r) || (r == cor.r && c < cor.c);
}
};
set<Cor> unfills;
void init() {
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
Map[i][j].num = 0;
Map[i][j].lefts.insert(LEFTS.begin(), LEFTS.end());
}
}
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
for (int k = 0; k < maxn; ++k) {
f[i][j][k + 1] = 0;
}
}
}
count = 0;
unfills.clear();
}
void input() {
for (int i = 0; i < maxn; ++i) {
scanf("%s", charMap[i]);
}
}
void transform() {
for (int i = 0; i < maxn; ++i) {
for (int j = 0; charMap[i][j] != '\0'; ++j) {
if (charMap[i][j] != '_') {
Map[i][j].num = charMap[i][j] - '0';
} else {
unfills.insert(Cor(i, j));
}
}
}
}
void setting(int r, int c, int num) {
Map[r][c].num = num;
for (int i = 0; i < maxn; ++i) {
Map[r][i].lefts.erase(num);
++f[r][i][num];
Map[i][c].lefts.erase(num);
++f[i][c][num];
}
int beginr = r / 3 * 3;
int beginc = c / 3 * 3;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
Map[i + beginr][j + beginc].lefts.erase(num);
++f[i + beginr][j + beginc][num];
}
}
}
void resume(int r, int c, int num) {
Map[r][c].num = 0;
for (int i = 0; i < maxn; ++i) {
if (--f[r][i][num] == 0) Map[r][i].lefts.insert(num);
if (--f[i][c][num] == 0) Map[i][c].lefts.insert(num);
}
int beginr = r / 3 * 3;
int beginc = c / 3 * 3;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (--f[i+beginr][j+beginc][num] == 0) Map[i + beginr][j + beginc].lefts.insert(num);
}
}
}
void predeal() {
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
if (Map[i][j].num != 0) {
setting(i, j, Map[i][j].num);
}
}
}
}
void dfs() {
if (unfills.empty()) {
// 达到一种目的状态
++count;
if (count > 1) return;
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
ans[i][j] = Map[i][j].num;
}
}
} else {
// 遍历unfills,寻找最小的
set<Cor>::iterator iter = unfills.begin(), target = unfills.begin();
while (iter != unfills.end()) {
const Cor &cor1 = *iter;
const Cor &cor2 = *target;
if (Map[cor1.r][cor1.c].lefts.size() < Map[cor2.r][cor2.c].lefts.size()) {
target = iter;
}
++iter;
}
Cor cor = *target;
unfills.erase(target);
set<int> _lefts(Map[cor.r][cor.c].lefts); //
set<int>::iterator iter1 = _lefts.begin();
while (iter1 != _lefts.end()) {
int num = *iter1;
setting(cor.r, cor.c, num);
dfs();
resume(cor.r, cor.c, num);
++iter1;
}
unfills.insert(cor);
}
}
int main() {
for (int i = 1; i <= 9; ++i) LEFTS.insert(i);
int t;
scanf("%d", &t);
for (int tt = 1; tt <= t; ++tt) {
// 初始化
init();
// 输入
input();
// 转化
transform();
// 预处理
predeal();
// 深搜
dfs();
if (count == 0) {
printf("Puzzle %d has no solution\n", tt);
} else if (count == 1) {
printf("Puzzle %d solution is\n", tt);
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
printf("%d", ans[i][j]);
}
printf("\n");
}
} else if (count > 1) {
printf("Puzzle %d has %d solutions\n", tt, count);
}
if (tt != t) printf("\n");
}
return 0;
}
浙公网安备 33010602011771号