Note:
- 给定的数独序列只包含数字
1-9
和字符'.'
。 - 你可以假设给定的数独只有唯一解。
- 给定数独永远是
9x9
形式的。
在看到题目的第一个思路是通过深度优先搜索+优先队列+动态规划的方法解决
根据三条规则找出每个空位可能的取值数量,通过优先队列选出可能数量最少的一个点进行填充,紧接着对优先队列进行更新,重新判断改变加入点后每个点不同的取值可能,接着按照DFS的思想对每个点进行依次填充,期间对于已经搜寻过的局势进行标记,再次出现时不进行搜索,直到所有点均被填充即为解。(要注意在DFS时全局变量的应用要格外小心!!!)
最后做出的结果正确,但是执行时间及内存消耗都较大。下附提交结果图及程序代码:
-
class Solution { public: struct Pos { int x, y; //坐标 //int depth; //深度 }; struct Class { Pos pos; //坐标 int num; //可能存在值的数量 bool possible[9] = { 0 }; //可能存在的值,0代表可取 bool operator <(const Class& s)const { if (num == s.num) //可能性数量相同时按照坐标排序 { if (pos.x == s.pos.x) return pos.x > s.pos.x; // else return pos.y > s.pos.y; // } else return num > s.num; //最小堆 } }; struct DT { int arry[9][9]; }; map<string, int>DT_Flag; //动态规划标志位,对每一种情况都有唯一的二维数组标识 map<int, DT>Source; //表示应放回优先队列的深度及情况 //priority_queue<Class> Home; //定义填写顺序 int Gong[3][3][9] = { 0 }; //表征每个3×3宫里已经填写的数(1代表填写) bool Finish = 0; int High; bool Updata(int h, priority_queue<Class> &updata) //更新优先队列 { for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { if (Source[h].arry[i][j] == 0) { Class D; D.pos.x = i; D.pos.y = j; D.num = 9; for (int k = 0; k < 9; k++) { if (Source[h].arry[i][k] != 0) { if (D.possible[Source[h].arry[i][k] - 1] == 0) { D.possible[Source[h].arry[i][k] - 1] = 1; //同行存在,改为不可取 D.num = D.num - 1; //cout << i << " " << j << ' ' << Source[h][i][k] << ' '<< D.possible[Source[h][i][k] -1]<<endl; } } } for (int k = 0; k < 9; k++) { if (Source[h].arry[k][j] != 0) { if (D.possible[Source[h].arry[k][j] - 1] == 0) { D.possible[Source[h].arry[k][j] - 1] = 1; //同列存在,改为不可取 D.num = D.num - 1; //cout << i << " " << j << ' ' << Source[h][k][j] << ' ' << D.possible[Source[h][k][j] - 1] << endl; } } } int off_x = i / 3; int off_y = j / 3; for (int k = 0; k < 3; k++) { for (int p = 0; p < 3; p++) { if (Source[h].arry[k + off_x*3][p + off_y*3] != 0) //同宫存在,改为不可取 { if (D.possible[Source[h].arry[k + off_x * 3][p + off_y * 3] - 1] == 0) { D.possible[Source[h].arry[k + off_x * 3][p + off_y * 3] - 1] = 1; D.num = D.num - 1; //cout << i << " " << j << ' ' << Source[h][k + off_x * 3][p + off_y * 3] << ' ' << D.possible[Source[h][k + off_x * 3][p + off_y * 3] - 1] << endl; } } } } if (D.num == 0) //有空但无值可取 return 0; //代表该状态不可解 updata.push(D); } } } return 1; } void DFS(int high) { priority_queue<Class> Home; if (!Updata(high, Home)) return; if (Home.empty()) { High = high; Finish = 1; } if (!Finish) { Class Save = Home.top(); for (int i = 0; i < Save.num; i++) { int j = 0; while (Save.possible[j] == 1) { j++; } Save.possible[j] = 1; //cout << "x=" << Save.pos.x << " y=" << Save.pos.y << " num = " << Save.num << " value = " << j + 1 << endl; Source[0].arry[Save.pos.x][Save.pos.y] = j + 1; string dt; for (int k = 0; k < 9; k++) { for (int p = 0; p < 9; p++) { char s[5] ; sprintf_s(s, "%d", Source[0].arry[k][p]); dt = dt.append(s); //cout << "zifu = " << dt << endl; } } if (DT_Flag.find(dt) == DT_Flag.end()) //如果未检测该情况,则检测 { DT_Flag[dt] = 1; DFS(0); } if (!Finish) Source[0].arry[Save.pos.x][Save.pos.y] = 0; else return; } } }
由于前期思路问题,优先队列其实没有必要,每次简单判断可能性即可。
-
通过看网上大佬们的解题思路,发现自己在规则判定上效率很低,我的规则判断在每个点上,而大佬的判断在行、列、宫上,这样更加简单。
-
经过改进后时间有显著减少
-
结果图及程序代码如下:
lass Solution {
public:
struct Class {
int x,y; //坐标
int num; //可能存在值的数量
bool possible[9] = { 0 }; //可能存在的值,0代表可取
};
map<string, int>DT_Flag; //动态规划标志位,对每一种情况都有唯一的二维数组标识
int Source[9][9]; //表示应放回优先队列的深度及情况
bool row[9][9] = { 0 }; //行填写数组
bool col[9][9] = { 0 }; //列填写数组
bool Gong[3][3][9] = { 0 }; //表征每个3×3宫里已经填写的数(1代表填写)
bool Finish = 0;
int release = 0;
bool Updata(Class &Min) //更新优先队列
{
Min.num = 10;
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
Class CC;
if (Source[i][j] == 0)
{
release++;
CC.x = i;
CC.y = j;
CC.num = 9;
for (int k = 0; k < 9; k++)
{
if (row[i][k] != 0 || col[j][k] != 0 || Gong[i / 3][j / 3][k] != 0)
{
CC.possible[k] = 1; //同行、列、宫存在,改为不可取
CC.num = CC.num - 1;
//cout << i << " " << j << ' ' << k + 1 << ' '<<endl;
}
}
if (CC.num == 0) //有空但无值可取
{
Min = CC;
return 0; //代表该状态不可解
}
else if (CC.num == 1)
{
Min = CC;
return 1;
}
else if (CC.num < Min.num)
Min = CC;
}
}
}
if (Min.num == 10)
{
release = 0;
//cout << "end" << endl;
}
return 1;
}
void DFS()
{
Class Min;
if (!Updata(Min))
return;
if (release == 0)
{
Finish = 1;
}
if (!Finish)
{
for (int i = 0; i < Min.num; i++)
{
int j = 0;
while (Min.possible[j] == 1)
{
j++;
}
Min.possible[j] = 1;
//cout << "x=" << save.x << " y=" << save.y << " num = " << save.num << " value = " << j + 1 << endl;
Source[Min.x][Min.y] = j + 1;
row[Min.x][Source[Min.x][Min.y] - 1] = 1; //更新
col[Min.y][Source[Min.x][Min.y] - 1] = 1;
Gong[Min.x / 3][Min.y / 3][Source[Min.x][Min.y] - 1] = 1;
string dt;
for (int k = 0; k < 9; k++)
{
for (int p = 0; p < 9; p++)
{
char s[5] ;
sprintf(s, "%d", Source[k][p]);
dt = dt.append(s);
}
}
//cout << "zifu = " << dt << endl;
if (DT_Flag.find(dt) == DT_Flag.end()) //如果未检测该情况,则检测
{
DT_Flag[dt] = 1;
DFS();
}
if (!Finish)
{
Source[Min.x][Min.y] = 0;
row[Min.x][j] = 0; //更新
col[Min.y][j] = 0;
Gong[Min.x / 3][Min.y / 3][j] = 0;
}
else
return;
}
}
}
void solveSudoku(vector<vector<char>>& board) {
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if(board[i][j] == '.')
Source[i][j] = 0;
else
{
Source[i][j] = board[i][j] - '0';
row[i][Source[i][j] - 1] = 1;
col[j][Source[i][j] - 1] = 1;
Gong[i / 3][j / 3][Source[i][j] - 1] = 1;
}
}
}
DFS();
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
board[i][j] = Source[i][j] + '0';
}
}
}
};