题目
- 从1~9的数,填到行列数为9的矩阵中,使他们行列都不出现相同数字,且每3*3行列中也不能出现相同数字
例如下面数独
5, 3, 4| 6, 7, 8| 9, 1, 2,
6, 7, 2| 1, 9, 5| 3, 4, 8,
1, 9, 8| 3, 4, 2| 5, 6, 7,
--------|----------|-----------
8, 5, 9| 7, 6, 1| 4, 2, 3,
4, 2, 6| 8, 5, 3| 7, 9, 1,
7, 1, 3| 9, 2, 4| 8, 5, 6,
--------|----------|----------
9, 6, 1| 5, 3, 7| 2, 8, 4,
2, 8, 7| 4, 1, 9| 6, 3, 5,
3, 4, 5| 2, 8, 6| 1, 7, 9,
现给出如下矩阵,0表示没填入数字,求出完整的数独矩阵
5, 3, 0, 0, 7, 0, 0, 0, 0,
6, 0, 0, 1, 9, 5, 0, 0, 0,
0, 9, 8, 0, 0, 0, 0, 0, 0,
8, 0, 0, 0, 6, 0, 0, 0, 3,
4, 0, 0, 8, 0, 3, 0, 0, 1,
7, 0, 0, 0, 2, 0, 0, 0, 6,
0, 6, 0, 0, 0, 0, 2, 8, 0,
0, 0, 0, 4, 1, 9, 0, 0, 5,
0, 0, 0, 0, 8, 0, 0, 7, 9,
结论
以下都只是我的思考方法
- 这个全排序的过程,所以使用f(f(…f(n))的递归方法,遍历整个表从1~9的填入测试能不能生成即可
- 为加快遍历,过滤无效的遍历,每行,每列,每3*3矩阵,生成他们的使用数字的情况,如果占用了,后面情况就不必遍历
代码
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <time.h>
#include <assert.h>
#include <random>
class Sudoku {
public:
static const int ONE_LINE_COUNT = 3;
static const int LINE_TOTAL_COUNT = ONE_LINE_COUNT * ONE_LINE_COUNT;
static const int ONE_MAT_COUNT = ONE_LINE_COUNT * ONE_LINE_COUNT;
static const int TOTAL_COUNT = ONE_MAT_COUNT * ONE_LINE_COUNT * ONE_LINE_COUNT;
Sudoku() {
memset(all_datas, 0, sizeof(all_datas));
memset(this->has_datas, false, sizeof(has_datas));
}
Sudoku(std::initializer_list<unsigned char> lst) {
memset(all_datas, 0, sizeof(all_datas));
memset(this->has_datas, false, sizeof(has_datas));
for (int i = 0; i < TOTAL_COUNT; ++i) {
auto it = lst.begin() + i;
if (it == lst.end())
break;
if (*it > 0) {
all_datas[i] = *it;
has_datas[i] = true;
}
}
}
void set_num(int row, int col, int value) {
int index = row * LINE_TOTAL_COUNT + col;
all_datas[index] = value;
this->has_datas[index] = true;
}
bool check() {
// 行不能重
char num_counts[9];
for (int i = 0; i < LINE_TOTAL_COUNT; ++i) {
memset(num_counts, 0, sizeof(num_counts));
for (int j = 0; j < LINE_TOTAL_COUNT; ++j) {
if(num_counts[all_datas[i*LINE_TOTAL_COUNT + j]-1]++ > 1)
return false;
}
}
// 列不能重
for (int i = 0; i < LINE_TOTAL_COUNT; ++i) {
memset(num_counts, 0, sizeof(num_counts));
for (int j = 0; j < LINE_TOTAL_COUNT; ++j) {
if (num_counts[all_datas[j*LINE_TOTAL_COUNT+i]-1]++ > 1)
return false;
}
}
// 3*3
for (int i = 0; i < ONE_LINE_COUNT; ++i) {
for (int j = 0; j < ONE_LINE_COUNT; ++j) {
int mat_index = i * ONE_LINE_COUNT + j;
memset(num_counts, 0, sizeof(num_counts));
for (int r = 0; r < ONE_LINE_COUNT; ++r) {
for (int c = 0; c < ONE_LINE_COUNT; ++c) {
int index = (r + i) * ONE_LINE_COUNT + c + j * ONE_LINE_COUNT;
if (num_counts[all_datas[index]-1]++ > 1)
return false;
}
}
}
}
return true;
}
bool _try_fill(int row, int col);
bool try_fill() {
memset(row_used, false, sizeof(row_used));
memset(col_used, false, sizeof(col_used));
memset(mat_used, false, sizeof(mat_used));
for (int i = 0; i < LINE_TOTAL_COUNT; ++i) {
for (int j = 0; j < LINE_TOTAL_COUNT; ++j) {
int index = i * LINE_TOTAL_COUNT + j;
int ti = this->all_datas[index];
if (ti > 0) {
this->set_used(i, j, ti - 1, true);
}
}
}
return _try_fill(0, 0);
}
inline bool is_next_num(int row, int col, int start) {
int mati_start = row / ONE_LINE_COUNT * LINE_TOTAL_COUNT * ONE_LINE_COUNT + col / ONE_LINE_COUNT * LINE_TOTAL_COUNT;
int row_start = row * LINE_TOTAL_COUNT;
int col_start = col * LINE_TOTAL_COUNT;
int row_index = start;
if (!row_used[row_index+row_start] && !col_used[row_index+col_start] && !mat_used[row_index+mati_start]) {
return true;
}
return false;
}
inline void set_used(int row, int col, int index, bool is_used) {
int mat_index = row / ONE_LINE_COUNT * LINE_TOTAL_COUNT * ONE_LINE_COUNT + col / ONE_LINE_COUNT * LINE_TOTAL_COUNT;
int row_index = row * LINE_TOTAL_COUNT + index;
int col_index = col * LINE_TOTAL_COUNT + index;
row_used[row_index] = is_used;
col_used[col_index] = is_used;
mat_used[mat_index+index] = is_used;
}
unsigned char all_datas[TOTAL_COUNT];
bool has_datas[TOTAL_COUNT];
bool row_used[TOTAL_COUNT];
bool col_used[TOTAL_COUNT];
bool mat_used[TOTAL_COUNT];
};
namespace std {
std::ostream& operator<<(std::ostream& os, const Sudoku& s) {
for (int i = 0; i < Sudoku::TOTAL_COUNT; ++i) {
if (i % Sudoku::LINE_TOTAL_COUNT == 0)
os << std::endl;
os << (int)s.all_datas[i] << ", ";
}
return os;
}
};
bool Sudoku::_try_fill(int row, int col){
int index = row * LINE_TOTAL_COUNT + col;
if (index >= TOTAL_COUNT) {
bool res = this->check();
return res;
}
if (!this->has_datas[index]) {
for (int i = 0; i < LINE_TOTAL_COUNT; ++i) {
bool res = this->is_next_num(row, col, i);
if (res) {
all_datas[index] = i+1;
this->set_used(row, col, i, true);
int next_row = row;
int next_col = col;
if (col + 1 >= LINE_TOTAL_COUNT) {
++next_row;
next_col = 0;
}
else
++next_col;
if (_try_fill(next_row, next_col)) {
return true;
}
this->set_used(row, col, i, false);
}
}
}else {
int next_row = row;
int next_col = col;
if (col + 1 >= LINE_TOTAL_COUNT) {
++next_row;
next_col = 0;
}
else
++next_col;
if (_try_fill(next_row, next_col))
return true;
}
return false;
}
void test_soduku() {
Sudoku s = {
5, 3, 0, 0, 7, 0, 0, 0, 0,
6, 0, 0, 1, 9, 5, 0, 0, 0,
0, 9, 8, 0, 0, 0, 0, 0, 0,
8, 0, 0, 0, 6, 0, 0, 0, 3,
4, 0, 0, 8, 0, 3, 0, 0, 1,
7, 0, 0, 0, 2, 0, 0, 0, 6,
0, 6, 0, 0, 0, 0, 2, 8, 0,
0, 0, 0, 4, 1, 9, 0, 0, 5,
0, 0, 0, 0, 8, 0, 0, 7, 9
};
std::cout << s << std::endl;
s.try_fill();
std::cout << s << std::endl;
}