一,数独的规则
- 横向上9个数字满足1-9不重复;
- 竖向上9个数字满足1-9不重复;
- 将大网格拆分为9个3*3的小网格,每个小网格内同样满足1-9不重复
二,生成数独的思路
首先准备一个空的数独,从第一个格子开始,按照数独的规则,把1-9依次试探性的填入,如果合法,就填入.如果不合法就判断下一个数合不合法,这样,一直到把第81个格子填完,就生成了一个完整的数独,但是因为这样生成的数独是一个固定的,所以,现在我们要随机把它打乱,根据数独的特性,在同一个小九宫格中的行和行之间交换位置,列与列之间交换位置,数独依然成立,打个比方,第0行和第2行,交换位置,第0列和第2列交换位置,数独依然成立.
这样随机打乱位置后,再根据要求的难度,随机挖空其中的一些格子,这样就生成了我们需要的数独题目了!
bool isValid(int row, int col, int num, vector<vector<char>>&board)
{
//检查行有没有重复的,如果有返回faulse
for (int i = 0; i < board.size(); i++)
{
if (num == board[row][i]-'0')
{
return false;
}
}
//检查列有没有重复的,如果有返回faulse
for (int i = 0; i < board.size(); i++)
{
if (num == board[i][col]-'0')return false;
}
//检查数字所在的方块有没有重复的,如果有返回faulse
int startrow = row / 3 * 3;
int startcol = col / 3 * 3;
int endrow = startrow + 3;
int endcol = startcol + 3;
for (int i = startrow; i < endrow; i++)
{
for (int j = startcol; j < endcol; j++)
{
if (num == board[i][j]-'0')return false;
}
}
return true;
}
void swapCol(int m, int n, vector<vector<char>>& board)
{
vector<char>temp(board.size(), ' ');
for (int i = 0; i < board.size(); i++)
{
temp[i] = board[i][m];
board[i][m] = board[i][n];
board[i][n] = temp[i];
}
}
void selectBlank(int nums, vector<vector<char>>& board)
{
srand(time(NULL));
while (nums)
{
int row = rand() % 9;
int col = rand() % 9;
if (board[row][col] != '.')
{
board[row][col] = '.';
nums--;
}
}
}
void create(int blank, vector<vector<char>>& board)
{
//小九宫格中的行和列交换,有以下9种交换方式
int choice[9][2] = { {0,1},{0,2},{1,2},{3,4},{3,5},{4,5},{6,7},{6,8},{7,8} };
srand(time(NULL));//设置动态种子
for (int j = 0; j < 3; j++)//J代表交换次数,也可以不用这个循环,就交换一次
{
int i = rand() % 9;//取0-8之间的随机值
board[choice[i][0]].swap(board[choice[i][1]]);//随机交换两行
swapCol(choice[i][0], choice[i][1], board);//随机交换两列
}
selectBlank(blank, board);//根据难度可以选择不同数量的空格
}
三,计算数独的思路
根据提供的数独,从第一个方格开始遍历,先判断是不是空,如果为空就把1-9依次试探性的填入,然后根据数独的规则,判断填入的数合不合法,如果合法就填入.然后递归遍历下一个方格,如果判断出下一个方格填入失败,说明现在填入的数是错的,需要回溯,然后把这个填入的数改为空,然后继续试探别的数字,如果所有数字试完都不行,那么就返回false,回溯到上一个数字.
这样直到把所有空格都遍历完成.
完整代码为:
// two.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <vector>
#include <string>
#include <cstdlib>
#include <ctime>
#include<Windows.h>
using namespace std;
class sudoku
{
public:
bool active = false;
sudoku(vector<vector<char>>&board)
{
// printBoard(board);
active = dfs(board, 0);
}
void printBoard(vector<vector<char>>&board)
{
cout << " |---|---|---|---|---|---|---|---|---| " << endl;
for (int i = 0; i < board.size(); i++)
{
for (int j = 0; j < board.size(); j++)
{
cout << " | " << board[i][j];
}
cout << endl;
cout << " ---|---|---|---|---|---|---|---|---| " << endl;
}
}
bool isValid(int row, int col, int num, vector<vector<char>>&board)
{
//检查行有没有重复的,如果有返回faulse
for (int i = 0; i < board.size(); i++)
{
if (num == board[row][i]-'0')
{
return false;
}
}
//检查列有没有重复的,如果有返回faulse
for (int i = 0; i < board.size(); i++)
{
if (num == board[i][col]-'0')return false;
}
//检查数字所在的方块有没有重复的,如果有返回faulse
int startrow = row / 3 * 3;
int startcol = col / 3 * 3;
int endrow = startrow + 3;
int endcol = startcol + 3;
for (int i = startrow; i < endrow; i++)
{
for (int j = startcol; j < endcol; j++)
{
if (num == board[i][j]-'0')return false;
}
}
return true;
}
void swapCol(int m, int n, vector<vector<char>>& board)
{
vector<char>temp(board.size(), ' ');
for (int i = 0; i < board.size(); i++)
{
temp[i] = board[i][m];
board[i][m] = board[i][n];
board[i][n] = temp[i];
}
}
void selectBlank(int nums, vector<vector<char>>& board)
{
srand(time(NULL));
while (nums)
{
int row = rand() % 9;
int col = rand() % 9;
if (board[row][col] != '.')
{
board[row][col] = '.';
nums--;
}
}
}
void create(int blank, vector<vector<char>>& board)
{
//小九宫格中的行和列交换,有以下9种交换方式
int choice[9][2] = { {0,1},{0,2},{1,2},{3,4},{3,5},{4,5},{6,7},{6,8},{7,8} };
srand(time(NULL));//设置动态种子
for (int j = 0; j < 3; j++)//J代表交换次数,也可以不用这个循环,就交换一次
{
int i = rand() % 9;//取0-8之间的随机值
board[choice[i][0]].swap(board[choice[i][1]]);//随机交换两行
swapCol(choice[i][0], choice[i][1], board);//随机交换两列
}
selectBlank(blank, board);//根据难度可以选择不同数量的空格
}
bool dfs(vector<vector<char>>&board, int start)//从0开始依次遍历81个格子,计算此数独
{
if (start == 81)//start=81,说明已经成功解出数独
{
return true;
}
else
{
bool ok = false;
int row = start / 9;//根据此时方格的序号,计算出此方格的行和列
int col = start % 9;
if (board[row][col] == '.')
{
for (int i = 1; i <= 9; i++)
{
if (isValid(row, col, i, board))//从1-9依次放入空格,并判断是否合法
{
board[row][col] = i + '0';//如果有数字合法,就写入该数字的字符
ok = dfs(board, start + 1);//判断此方格的下一个方格是否成功写入
if (!ok)//如果它的下一个方格是不合法的,说明它现在填入的数,不是正确的解,需回溯
{
board[row][col] = '.';//回溯
}
}
}
}
else
{
ok = dfs(board, start + 1);
}
return ok;
}
}
};
int main()
{
/*vector<vector<char>>board = { vector<char>{'5','3','.','.','7','.','.','.','.'},
vector<char>{'6','.','.','1','9','5','.','.','.'},
vector<char>{'.','9','8','.','.','.','.','6','.'},
vector<char>{'8','.','.','.','6','.','.','.','3'},
vector<char>{'4','.','.','8','.','3','.','.','1'},
vector<char>{'7','.','.','.','2','.','.','.','6'},
vector<char>{'.','6','.','.','.','.','2','8','.'},
vector<char>{'.','.','.','4','1','9','.','.','5'},
vector<char>{'.','.','.','.','8','.','.','7','9'}, };*/
vector<vector<char>>blank(9, vector<char>(9,'.'));//创建一个9*9的空二维数组
sudoku s(blank);
s.create(50,blank);//创建50个空格的数独
if (s.active)
{
cout << "创建的数独为:" << endl;
s.printBoard(blank);
}
else
cout << "数独表不合法,没有解!" << endl;
s.dfs(blank, 0);//解数独函数
if (s.active)
{
cout << "此数独的解是:" << endl;
s.printBoard(blank);
}
else
cout << "解题失败!" << endl;
return 0;
}