目录
一,标准数独(规则数独)
1,规则
数独盘面是个九宫,每一宫又分为九个小格。(宫即3*3的正方形)
在这八十一格中给出一定的已知数字,利用逻辑和推理,在其他的空格上填入1-9的数字。
使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。
2,隐含规则
标准数独有一个隐含的规则,就是根据所给的数字,必须有且只有一个解。
在这个前提下,可以证明,至少需要给出17个数字。
17个数字的数独也不难设计出来,下面的题目中就有。
3,对称性
规则数独,对称性自然只能指数字的位置分布了。
规则数独和不规则数独在数字的分布上其实差别不大,也是那几种对称性,后面给出了一个示例。
相对而言,规则数独要想给出对称的位置分布的初始局面,比较简单。
而且因为格子画的本身就很对称,所以整体看起来很对称,这一点,不规则数独一般都做不到。


还有斜线数独也有很强的对称性。
4,斜线数独

这个数独看起来给的数也不少了,有27个,但是很难。
想了半天,一个都推不出来,还好我有下面的代码。
输入:
2..8..6..
.6..2..9.
..4..6..7
1..6..9..
.7..3..8.
..9..5..1
5..4..3..
.1..5..4.
..2..8..9
输出:
293817654
761524893
854396127
138642975
475931286
629785431
587469312
916253748
342178569
这就是这个数独的解了。
二,标准数独校验、求解
力扣 36. 有效的数独
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

上图是一个部分填充的有效的数独。
数独部分空格内已填入了数字,空白格用 '.' 表示。
示例 1:
输入:
[
["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
输出: true
示例 2:
输入:
[
["8","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
输出: false
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
说明:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
给定数独序列只包含数字 1-9 和字符 '.' 。
给定数独永远是 9x9 形式的。
class Solution {
public:
bool ok(vector<vector<char>>& board, int i, int j, int k)
{
for (int jj = 0; jj < 9; jj++)if (jj!=j && board[i][jj] == k)return false;
for (int ii = 0; ii < 9; ii++)if (ii!=i && board[ii][j] == k)return false;
int x = i / 3 * 3, y = j / 3 * 3;
for (int ii = x; ii < x + 3; ii++)for (int jj = y; jj < y + 3; jj++)
if ((ii!=i||jj!=j) && board[ii][jj] == k)return false;
return true;
}
bool isValidSudoku(vector<vector<char>>& board) {
for(int i=0;i<board.size();i++){
for(int j=0;j<board[0].size();j++){
if(board[i][j]!='.' && !ok(board,i,j,board[i][j])){
return false;
}
}
}
return true;
}
};
POJ 3074 Sudoku
In the game of Sudoku, you are given a large 9 × 9 grid divided into smaller 3 × 3 subgrids. For example,

Given some of the numbers in the grid, your goal is to determine the remaining numbers such that the numbers 1 through 9 appear exactly once in (1) each of nine 3 × 3 subgrids, (2) each of the nine rows, and (3) each of the nine columns.
Input
The input test file will contain multiple cases. Each test case consists of a single line containing 81 characters, which represent the 81 squares of the Sudoku grid, given one row at a time. Each character is either a digit (from 1 to 9) or a period (used to indicate an unfilled square). You may assume that each puzzle in the input will have exactly one solution. The end-of-file is denoted by a single line containing the word “end”.
Output
For each test case, print a line representing the completed Sudoku puzzle.
Sample Input
.2738..1..1...6735.......293.5692.8...........6.1745.364.......9518...7..8..6534.
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
end
Sample Output
527389416819426735436751829375692184194538267268174593643217958951843672782965341
416837529982465371735129468571298643293746185864351297647913852359682714128574936
这个题目,就是直接输入标准数独,输出数独的解。
题目里面有说You may assume that each puzzle in the input will have exactly one solution.
实际上这是标准数独必须满足的规定。
代码:
#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
int list[10][10];
bool ok(int i, int j, int k)
{
for (int jj = 1; jj < 10; jj++)if (list[i][jj] == k)return false;
for (int ii = 1; ii < 10; ii++)if (list[ii][j] == k)return false;
int x = (i - 1) / 3 * 3, y = (j - 1) / 3 * 3;
for (int ii = x + 1; ii <= x + 3; ii++)for (int jj = y + 1; jj <= y + 3; jj++)
if (list[ii][jj] == k)return false;
return true;
}
bool trys(int i, int j)
{
if (j == 10)
{
i++;
j = 1;
}
if (i == 10)return true;
if (list[i][j])return trys(i, j + 1);
for (int k = 1; k < 10; k++)
{
if (ok(i, j, k))
{
list[i][j] = k;
if (trys(i, j + 1))return true;
}
}
list[i][j] = 0;
return false;
}
int main()
{
char c,s[100];
while (1)
{
scanf("%s", &s);
if (s[0] == 'e')return 0;
for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++)
{
c = s[i * 9 + j - 10];
if (c == '.')list[i][j] = 0;
else list[i][j] = c - '0';
}
trys(1, 1);
for (int i = 1; i < 10; i++)for (int j = 1; j < 10; j++)printf("%d", list[i][j]);
printf("\n");
}
return 0;
}
这个代码已经飞快了,解一个数独大概是几毫秒,然而还是超时了。
于是我用dancing links来做,AC了。
#include <iostream>
#include <vector>
#include <map>
#include <iomanip>
#include <string>
#include <algorithm>
#include <cmath>
#include <stack>
using namespace std;
class DancingLink
{
public:
vector<int>rows;//覆盖选中的行,值的范围是从1到m
DancingLink(int m, int n, int maxNum)
{
this->m = m, this->n = n;
rhead.resize(m + 1), nums.resize(n + 1);
row.resize(maxNum), col.resize(maxNum);
up.resize(maxNum), down.resize(maxNum), lef.resize(maxNum), rig.resize(maxNum);
sc.resize(m), rows.resize(m);
for (int i = 0; i <= n; i++)
{
up[i] = i, down[i] = i;
lef[i] = i - 1, rig[i] = i + 1;
row[i] = 0, col[i] = i, nums[i] = 0;
}
lef[0] = n, rig[n] = 0, nums[0] = INT_MAX;
scid = 0, rowsid = 0;
key = n;
for (int i = 0; i <= m; i++)rhead[i] = 0;
}
void push(int r, int c)//新增坐标在(r,c)的一个节点
{
row[++key] = r, col[key] = c;
up[key] = c, down[key] = down[c];
up[down[c]] = key, down[c] = key;
if (rhead[r] == 0)rhead[r] = lef[key] = rig[key] = key;
else
{
lef[key] = rhead[r], rig[key] = rig[rhead[r]];
lef[rig[rhead[r]]] = key, rig[rhead[r]] = key;
}
nums[c]++;
}
bool dfs()
{
while (true) {
if (rig[0] == 0) {
rows.resize(rowsid);
return true;
}
int c = min_element(nums.begin(), nums.end()) - nums.begin();
del(c);
while (c = down[c]) {
if (c > n)break;
reback(col[c]);
c = sc[--scid];
rowsid--;
for (int j = rig[c]; j != c; j = rig[j])reback(col[j]);
}
sc[scid++]=c;//记录选中id
rows[rowsid++]=row[c];
for (int j = rig[c]; j != c; j = rig[j])del(col[j]);
}
return false;
}
private:
inline void del(int c)//删除第c列的所有元素和他们所在行的所有元素
{
lef[rig[c]] = lef[c], rig[lef[c]] = rig[c];
for (int i = down[c]; i != c; i = down[i])
for (int j = rig[i]; j != i; j = rig[j])
down[up[j]] = down[j], up[down[j]] = up[j], nums[col[j]]--;
nums[c] = INT_MAX;
}
inline void reback(int c)//完全回退del操作
{
lef[rig[c]] = rig[lef[c]] = c, nums[c] = 0;
for (int i = down[c]; i != c; i = down[i]) {
for (int j = rig[i]; j != i; j = rig[j])
down[up[j]] = up[down[j]] = j, nums[col[j]]++;
nums[c]++;
}
}
private:
int m, n, key;
vector<int>row, col;//每个节点的行,列
vector<int>rhead;//每行第一个节点的id
vector<int>up, down, lef, rig;//每个节点上下左右的节点id
vector<int>nums;//每一列的元素个数
vector<int>sc;
int scid, rowsid;
};
string Sudoku(string s, char cEmpty = '.')
{
int num = 0;
for (int i = 0; i < 81; i++)if (s[i] != cEmpty)num++;
int m = (81 - num) * 9 + num;
int n = 81 * 4;
DancingLink d(m, n, m * 4 + n + 5);
int row = 0;
map<int, int>mrow;
mrow[0] = -1;
for (int i = 0; i < 81; i++) {//第i个格子
char c = s[i];
int low = 0, high = 8;
if (c != cEmpty)low = high = c - '1';//第i个格子的搜索值域
for (int x = low; x <= high; x++) {
d.push(++row, i + 1), d.push(row, i / 9 * 9 + x + 81 + 1);
d.push(row, i % 9 * 9 + x + 162 + 1), d.push(row, (i / 27 * 3 + i % 9 / 3) * 9 + x + 243 + 1);
mrow[row] = i;
}
}
if (!d.dfs())return "";
string ans = s;
for (int i = 0; i < d.rows.size();i++) {
int row = d.rows[i];
int id = mrow[row];
if (s[id] == cEmpty) {

本文详细介绍了数独的基本规则、隐含规则及对称性,包括标准数独和斜线数独,并列举了多个数独求解的编程题目,如LeetCode的36和37题,POJ的3074和2676题,以及HDU的1426题。文章还讨论了解决数独的常用方法,如摒除法、鱼结构等,并分享了个人参与的湖南省首届高校联合数独大赛的经历。
最低0.47元/天 解锁文章
3831

被折叠的 条评论
为什么被折叠?



