应该算是深度优先搜索吧?看到sicily 上说时间为10s ,直接来个暴力的! 但是 Time Limit Exceeded 了,先来暴力的代码:
#include<iostream>
#include<cstring>
#include<stdio.h>
using namespace std;
//表示九宫格的每个格子能填写的数字, isvaild[0][0][0]=true 表示第一个格子能够填‘1’
bool isvaild[9][9][9];
//如果有解法,则存储第一种解法
char ans[9][9];
//题目
char sudoku[9][9];
//答案个数
int anscont;
//从输入中获得题目,并对相应的可访问性,格子的可填的数字访问范围做出设置
void input()
{
for(int i = 0; i < 9; i++)
{
for(int j = 0; j < 9; j++)
{
//输入题目
cin >> sudoku[i][j];
//输入的是数字
if(sudoku[i][j] != '_')
{
//(i, j)该格子设置为已访问
//visited[i][j] = true;
//每当输入了一个数字,会使得某些位置可填的数字范围发生改变
//改变该数字所在的行、列
for(int k = 0; k < 9; k++)
{
//i 行的不能再填写此数字
//因为sudoku[i][j] - '0' 得到的时输入的数字,而储存的位置为相应的-1
isvaild[i][k][sudoku[i][j] - '0' - 1] = false;
isvaild[k][j][sudoku[i][j] - '0' - 1] = false;
}
//改变该数字所在的小九宫格
//i, j 分别表示所在小九宫格的起始行与列
int row = i/3*3;//如2/3*3=0*3=0; 5/3*3=1*3=3
int column = j/3*3;
//m 移动行号, n 移动列号
for(int m = 0; m < 3; m++)
{
for(int n = 0; n < 3; n++)
{
isvaild[row + m][column + n][sudoku[i][j] - '0' - 1] = false;
}
}
}
}
}
}
//开始寻找解法,利用深度优先搜索
//参数表示循环的次数,当循环到depth = 81时即搜索结束(表示当前填写的是哪个格子)
//返回值为解法的个数
int DFS(int depth)
{
if(depth == 82)//已经填满了81个格子,搜索结束
{
if(anscont == 0)//目前还未找到解法,此为第一个
{
for(int p = 0; p < 9; p++)
{
for(int q = 0; q < 9; q++)
{
ans[p][q]= sudoku[p][q];//获得第一个解法
}
}
}
return 1;//方法数+1
}
//搜索仍需继续
//第 depth 个格子所在的行列
int column = (depth - 1)%9;//例如第9个格子(0,8), (9-1)%9 = 8,表示在第8列
int row = (depth - 1)/9; //例如第9个格子(0,8), (9 - 1)/9 = 0,表示在第0行
//如果当前的格子已存有数字,则跳过,继续下一次的循环
if(sudoku[row][column] != '_')
{
return DFS(depth + 1);//返回值为当确定下个格子时能有的解法个数
}
//如果到了这里,说明该格子上的不是数字
//看这个格子能够填写什么数字
//在此先“剪枝”,查看是否能够填数字,否则到此结束,说明此解法不存在
int i;
for(i = 0; i < 9; i++)
{
if(isvaild[row][column][i] == true)//该格子能够填i+1
{
break;
}
}
if(i == 9)//循环没有跳出
{
return 0; //解法数为0
}
//到此已经说明了,该格子是‘_’,也可以填写数字
//开始遍历能填写的数字,并搜索下一个格子
//anscont 记录当前位置能寻找到的路径数
int anscont = 0;
for(i = 0; i < 9; i++)
{
if(isvaild[row][column][i] == true)//该格子能够填i+1
{
sudoku[row][column] = i + '1';//i = 0表示可以填写1
int changeROW[81];
int changeCOLUMN[81];
int size = 0;
//每当输入了一个数字,会使得某些位置可填的数字范围发生改变
//改变该数字所在的行
for(int k = 0; k < 9; k++)
{
if(isvaild[row][k][i] == true)
{
isvaild[row][k][i] = false;
changeROW[size] = row;//记录改变了的行号与列号
changeCOLUMN[size++] = k;
}
if(isvaild[k][column][i] == true)
{
isvaild[k][column][i] = false;
changeROW[size] = k;//记录改变了的行号与列号
changeCOLUMN[size++] = column;
}
}
//cout << size << endl;
//改变该数字所在的小九宫格
//m 移动行号, n 移动列号
int Setrow = row/3*3;//所在小九宫图的起始行
int Setcolumn = column/3*3;//所在小九宫图的起始列
for(int m = 0; m < 3; m++)
{
for(int n = 0; n < 3; n++)
{
if(isvaild[Setrow + m][Setcolumn + n][i] == true)
{
isvaild[Setrow + m][Setcolumn + n][i] = false;
changeROW[size] = Setrow + m;
changeCOLUMN[size++] = Setcolumn + n;
}
}
}
anscont = anscont + DFS(depth + 1);//当该格子定下后,开始访问下一个格子
for(int g = 0; g<size; g++)
{
isvaild[changeROW[g]][changeCOLUMN[g]][i] = true;
}
sudoku[row][column] = '_';
}
}
return anscont;
}
int main()
{
int testNum;//题目个数
/*cin >> testNum;*/
scanf("%d",&testNum);
int i = 0;
while(testNum--)
{
i++;//第几个题目;
//九宫格的每个格子均可以填任意数字
memset(isvaild, true, sizeof(isvaild));
anscont = 0;
//开始初始化题目
input();
//开始寻找解法,从第一个格子开始
anscont = DFS(1);
if(anscont == 1)//如果只有一个解法
{
printf("Puzzle %d solution is",i);
//cout << "Puzzle " << i << " solution is" << endl;
for(int m = 0; m < 9; m++)
{
for(int n = 0; n < 9; n++)
{
printf("%c",ans[m][n]);
/*cout << ans[m][n];*/
}
//cout << endl;//输出完一行
printf("\n");
}
}
else
{
if(anscont == 0)
{
printf("Puzzle %d has no solution\n",i);
/*cout << "Puzzle " << i << " has no solution" << endl;*/
}
else//多解
{
printf("Puzzle %d has %d solutions\n",i, anscont);
//cout << "Puzzle " << i << " has " << anscont << " solutions" << endl;
}
}
}
return 0;
}
没办法,只能想办法优化一下了,选择可能性最小的格子开始,并在中途剪枝,时间:0.15s
还有就是,不能用memset() 函数来为一个非字符型数组赋初值是不允许的,这也让我纠结了好久!!
最坑爹的是最后竟然是表达有问题,又提交了好久!!!
#include<iostream>
#include<cstring>
#include<stdio.h>
using namespace std;
bool isvaild[9][9][9];
char ans[9][9];
char sudoku[9][9];
int anscont;
bool flag;
//表示从第一个(count[0])格子到第81个格子能填的数字个数的数组
//count[81]用来比较
int count[82];
void input()
{
for(int i = 0; i < 9; i++)
{
for(int j = 0; j < 9; j++)
{
cin >> sudoku[i][j];
if(sudoku[i][j] != '_')
{
count[i*9 + j] = 0;//表示该格子已确定了数字,如sudoku[1][2]为第12个格子(count[11])
for(int k = 0; k < 9; k++)
{
if(isvaild[i][k][sudoku[i][j] - '0' - 1] == true)//为了不重复
{
isvaild[i][k][sudoku[i][j] - '0' - 1] = false;
count[i*9 + k]--;
}
if(isvaild[k][j][sudoku[i][j] - '0' - 1] == true)
{
isvaild[k][j][sudoku[i][j] - '0' - 1] = false;
count[j + k*9]--;
}
}
int startRow = i/3*3;
int startColumn = j/3*3;
for(int m = 0; m < 3; m++)
{
for(int n = 0; n < 3; n++)
{
if(isvaild[startRow + m][startColumn + n][sudoku[i][j] - '0' - 1] == true)
{
isvaild[startRow + m][startColumn + n][sudoku[i][j] - '0' - 1] = false;
count[(startRow + m)*9 + startColumn + n]--;
}
}
}
}
}
}
}
int DFS()
{
int minValIndex = 81;
for(int i = 0; i < 81; i++)//找出可能性最小的格子
{
int ROW = i/9;
int COLUMN = i%9;
if(sudoku[ROW][COLUMN] != '_')
{
continue;//已填数字
}
//下面的都是还没填数字的
if(count[i] == 0)//可以填数字,但是能填的个数为0,表明失败
{
return 0;
}
if(count[i] > 0 && count[i] < count[minValIndex])//非0表明还未确定数字
{
minValIndex = i;
}
}
if(minValIndex == 81)//所有的格子均已确定了数字
{
if(flag == true)//第一个
{
flag = false;
memcpy(ans, sudoku,sizeof(sudoku));
}
return 1;
}
int Anscont = 0;//确定当前位置后的解法数
//该格子仍未确定数字,从可能的数字开始循环
int row = minValIndex / 9;//如sudoku[1][2] 即 count[11] ,row = 11/9 = 1; column = 11%9 = 2;
int column = minValIndex % 9;
for(int i = 0; i < 9; i++)
{
if(isvaild[row][column][i])//该格子能填i + 1
{
int changeCount[81];
for(int j = 0; j < 81; j++)
{
changeCount[j] = count[j];
}
sudoku[row][column] = i + '1';//填充数字
isvaild[row][column][i] = false;
count[row*9 + column] = 0;//已确定数字
//记录哪些位置发生了改变,后面要恢复
int changeRow[81];
int changeColumn[81];
int size = 0;
//行列
for(int j = 0; j < 9; j++)
{
if(isvaild[row][j][i] == true)
{
isvaild[row][j][i] = false;
changeRow[size] = row;
changeColumn[size++] = j;
count[row*9 + j]--;//所在行
}
if(isvaild[j][column][i] == true)
{
isvaild[j][column][i] = false;
changeRow[size] = j;
changeColumn[size++] = column;
count[column + j*9]--;//所在列
}
}
//小九宫图
int startRow = row / 3 * 3;
int startColumn = column / 3 * 3;
for(int j = 0; j < 3; j++)
{
for(int k = 0; k < 3; k++)
{
if(isvaild[startRow + j][startColumn + k][i] == true)
{
isvaild[startRow + j][startColumn + k][i] = false;
changeRow[size] = startRow + j;
changeColumn[size++] = startColumn + k;
count[(startRow + j)*9 + startColumn + k]--;//所在九宫图
}
}
}
Anscont = Anscont + DFS();//搜索下个位置
//恢复
sudoku[row][column] = '_';
isvaild[row][column][i] = true;
for(int j = 0; j < size; j++)
{
isvaild[changeRow[j]][changeColumn[j]][i] = true;
}
for(int j = 0; j < 81; j++)
{
count[j] = changeCount[j];
}
}
}
return Anscont;
}
int main()
{
int testNum;
scanf("%d",&testNum);
for(int i = 1; i <= testNum; i++)
{
memset(isvaild, true, sizeof(isvaild));
//用memset对非字符型数组赋初值是不可取的
//memset(count,9,81*sizeof(int));//每个格子初始化,能填9个数字,0表示已经确定了数字
for(int j = 0; j < 81; j++)
{
count[j] = 9;
}
count[81] = 100;
anscont = 0;
input();
flag = true;
anscont = DFS();
if(anscont == 1)//如果只有一个解法
{
printf("Puzzle %d solution is\n",i);
for(int m = 0; m < 9; m++)
{
for(int n = 0; n < 9; n++)
{
printf("%c",ans[m][n]);
}
printf("\n");
}
}
else
{
if(anscont == 0)
{
printf("Puzzle %d has no solution\n",i);
}
else//多解
{
printf("Puzzle %d has %d solutions\n",i, anscont);
}
}
if(i != testNum)
printf("\n");
}
return 0;
}