某笔试的题目:
锯齿数独3x3
简单来说就是给你一个填了某些字的数独,告诉你哪3个点属于一个宫,然后同行同列同宫不能有重复。
接下来给定几组数据,判断是否有解,唯一解则输出Unique和该解,多解则输出Mutiple,无解输出无解。
思路:
经典的矩阵型,dfs遍历的方式。自己写的时候遇上了几个问题,现在在此总结一下。
3行3列代表数独信息,下面3行,每行代表一个宫的三个点。
输入数据如下:
4
*2*
1*2
***
0 0 0 1 1 0
0 2 1 1 1 2
2 0 2 1 2 2
遍历的方式:使用dfs遍历,然后上下左右遍历,
如果点超过边界则返回。
如果访问过则返回。防止再次访问。
对于点有两种情况,一种是没有赋值的点,那我们就赋值,赋值时从1到3依次判断,判断该点是否满足行列宫没有重复。
已有值的点,那么无需再访问,直接返回即可。
(一开始我是这么想的,发现不能这样:原因如下:
例如:
这个左上角的点去dfs遍历时,右边和下面的点都已赋值,如果直接返回,会出现dfs直接无法遍历整个图。因为(0,0)这个点与外界不连通。因此为了实现走出外面,即使该值有点也要进行向外dfs:
这样即可保证dfs一次初始点(0,0)一定能遍历完整张图。
上面是对于该点已有值的情况,接下来介绍对于该点没有值的情况:
如果没有值就从1到3依次往里面试看是否满足同行同列同宫没有相同的数,找到第一个成功的数就填入。
填入之后则将checkNum++(checkNum是记录已经填满的数字个数)。
找到了一个点之后,我们就以此为基础,继续dfs:如下图红框所示
而当dfs结束时,也就是回溯后, 需要将值返回成原来的样子:如下图红框所示
那么如何判断是否找到解?找到解之后需要做什么?
判断是否找到解的方式比较简单,只需要在填入数字,增加checkNum时,判断是否为9:
找到解后,将ans的值++。
由于单解情况下需要输出解,而回溯时会把填的数字清空,所以我们需要把该解记录下来,用一个successArray。
此处如何判断多解的思路?
此处无需找出全部解,只需要判断是否是多解即可。
因此,如果找到了一个解,那么只需要将这个解记录下来,然后之后如果找到了解(即checkNum为9)的话,就判断这两个解是否相同,如果不相同则说明有多解。
代码如下:
#include<iostream>
using namespace std;
class Point {
public:
int x, y;
Point() {
}
Point(int x1, int y1) {
x = x1, y = y1;
}
};
class GongClass {
public:
Point gongPoint[3];
};
class ShuDu {
int array[3][3];
bool visit[3][3];
GongClass Gong[3];
int checkNum = 0;//已经写完的数
int successArray[3][3];
public:
int hasAns = 0;
void ShuDuInit() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
char num;
cin >> num;
if (num != '*') {
checkNum++;
array[i][j] = int(num - '0');
}
else array[i][j] = 0;
visit[i][j]=false;
}
}
for (int i = 0; i < 3; i++) {
for (int k = 0; k < 3; k++) {
int x, y;
cin >> x >> y;
Point point;
point.x = x;
point.y = y;
Gong[i].gongPoint[k] = point;
}
}
//dfsCheck(Point(0, 0));
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cout << "dfsPoint:" << i << " " << j << endl;
printArray();
dfsCheck(Point(i, j));
}
}
/*cout << checkNum << endl;
cout << hasAns << endl;*/
}
bool CheckRow(Point point, int num) {
int x = point.x;
int y = point.y;
for (int j = 0; j < 3; j++) {
if (array[x][j] == num) {
//cout << "chongfuRow" << x << " " << j << endl;
return false;
}
}
return true;
}
bool CheckCol(Point point, int num) {
int x = point.x;
int y = point.y;
for (int i = 0; i < 3; i++) {
if (array[i][y] == num) {
//cout << "chongfuCol " << i << " " << y << endl;
//cout << "array i y:"<<array[i][y] << endl;
return false;
}
}
return true;
}
bool CheckGong(Point point, int num) {
GongClass pointInGong = findGong(point);
for (int i = 0; i < 3; i++) {
int x = pointInGong.gongPoint[i].x;
int y = pointInGong.gongPoint[i].y;
if (array[x][y] == num)return false;
}
return true;
}
GongClass findGong(Point point) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (Gong[i].gongPoint[j].x == point.x&&Gong[i].gongPoint[j].y == point.y)
return Gong[i];
}
}
return Gong[0];
}
void dfsCheck(Point point) {
int x = point.x;
int y = point.y;
if (point.x < 0 || point.x >= 3 || point.y < 0 || point.y >= 3)return;
if (visit[x][y] == true)return;
/*if (visit[x+1][y] == false)dfsCheck(Point(x + 1, y));
if (visit[x][y+1] == false)dfsCheck(Point(x, y + 1));
if (visit[x - 1][y] == false)dfsCheck(Point(x - 1, y));
if (visit[x][y-1] == false)dfsCheck(Point(x, y - 1));*/
//cout << "point.x:" << x << " point.y:" << y << endl;
visit[x][y] = true;//进入一个点则设置访问
bool checkFlag = false;//检查是否有满足的数字可以填入
if (array[x][y] == 0) {
for (int index = 1; index <= 3; index++) {
//检查该数字是否满足每行每列每宫的要求
if (CheckRow(point, index) && CheckCol(point, index) && CheckGong(point, index)) {
array[point.x][point.y] = index;
checkNum++;
checkFlag = true;
cout << "success Point [" << x << "][" << y << "]"<< ":" << index << endl;
if (checkNum == 9) {
hasAns++;
if (hasAns > 1) {
if (checkSameArray()) {
cout << "SameArray" << endl;
hasAns--;
break;
}
}
cout << "hasAns" << endl;
//由于回溯时会删去visit的记录和做好标记的结点,所以需要删除值,但是因为需要打印,所以需要将该成功的值保存
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
successArray[i][j] = array[i][j];
}
printArray();
}
//dfsCheck(Point(x + 1, y));
//dfsCheck(Point(x, y + 1));
//dfsCheck(Point(x - 1, y));
//dfsCheck(Point(x, y - 1));
//array[point.x][point.y] = 0;
//checkNum--;
//visit[x][y] = false;
break;
}
}
}
else {
//visit[x][y] = true;
dfsCheck(Point(x + 1, y));
dfsCheck(Point(x, y + 1));
dfsCheck(Point(x - 1, y));
dfsCheck(Point(x, y - 1));
return;
}
//如果里面不能填入数字,说明前一步有问题,因此应该要返回,并且重置访问
if (checkFlag == false) {
visit[x][y] = false;
return;
}
dfsCheck(Point(x + 1, y));
dfsCheck(Point(x, y + 1));
dfsCheck(Point(x - 1, y));
dfsCheck(Point(x, y - 1));
array[point.x][point.y] = 0;
checkNum--;
visit[x][y] = false;
return;
}
void printSuccessArray() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cout << successArray[i][j];
}
cout << endl;
}
}
void printArray() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
cout << array[i][j];
}
cout << endl;
}
}
bool checkSameArray() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (array[i][j] != successArray[i][j])return false;
}
}
return true;
}
};
int main() {
int t;
cin >> t;
while (t--) {
ShuDu shuDu;
shuDu.ShuDuInit();
if (shuDu.hasAns == 1) {
cout << "Unique" << endl;
shuDu.printSuccessArray();
}
else if(shuDu.hasAns>1){
cout << "Multiple" << endl;
}
else cout << "NO"<<endl;
}
};
/*
1
*2*
1*2
***
0 0 0 1 1 0
0 2 1 1 1 2
2 0 2 1 2 2
4
*2*
1*2
***
0 0 0 1 1 0
0 2 1 1 1 2
2 0 2 1 2 2
**3
***
***
0 0 1 0 1 1
0 1 0 2 1 2
2 0 2 1 2 2
**3
1**
**2
0 0 1 0 1 1
0 1 0 2 1 2
2 0 2 1 2 2
3*3
1**
**2
0 0 1 0 1 1
0 1 0 2 1 2
2 0 2 1 2 2
*/