数独是一种传统益智游戏,你需要把一个9 × 9的数独补充完整,使得图中每行、每列、每个3 × 3的九宫格内数字1~9均恰好出现一次。
请编写一个程序填写数独。
输入格式
输入包含多组测试用例。
每个测试用例占一行,包含81个字符,代表数独的81个格内数据(顺序总体由上到下,同行由左到右)。
每个字符都是一个数字(1-9)或一个”.”(表示尚未填充)。
您可以假设输入中的每个谜题都只有一个解决方案。
文件结尾处为包含单词“end”的单行,表示输入结束。
输出格式
每个测试用例,输出一行数据,代表填充完全后的数独。
输入样例:
.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
输出样例:
527389416819426735436751829375692184194538267268174593643217958951843672782965341
416837529982465371735129468571298643293746185864351297647913852359682714128574936
链接:https://www.acwing.com/problem/content/description/168/
题解: DFS、用row、col、cell 三个数组,分别表示每行每列每个九宫格中可放的数字。 利用的是二进制的存储方式:
000000001 ->1 000000010 -》2 ,即看二进制中1在哪一位上,就表示那一位的映射的1、2、3…是可选的。
所以 每个格子可放的就是row、col、cell三个数字的交集(由交集也可想到用&即利用二进制)。
优化DFS:剪枝、记忆化、优化搜索顺序(优先搜索决策少的枚举量)
本题中使用了剪枝、优化搜索顺序技巧,每次搜索都是先从所有格子中选择出可选数最少的格子进行搜索。
#include <iostream>
using namespace std;
const int N=9;
int row[N],col[N],cell[N/3][N/3];
int hashtable[1 << N];//1-9的对应二进制数;
//优化搜索顺序,优先选择决策少的格子进行dfs
int ones[1 << N]; //记录二进制数对应的可选状态数
char str[100];
inline int lowbit(int j){
return j&-j;
}
void init(){
//对row、col、cell进行初始化
for(int i=0;i<N;i++) row[i]=col[i]=(1<<N)-1;
for(int i=0;i<3;i++)
{
for(int j=0; j<3;j++)
{
cell[i][j]=(1 << N) - 1;
}
}
}
inline int get(int x,int y)
{
return row[x] & col[y] & cell[x/3][y/3];
}
bool dfs(int cnt){
if(cnt == 0) return true;
//找出最少决策的格子
int minv=10;
int mx,my;
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
{
if(str[i*N+j]=='.')
{
if(ones[get(i,j)] < minv)
{
minv=ones[get(i,j)];
mx=i;
my=j;
}
}
}
}//找出了先要修改的mx,my位置
for(int i=get(mx,my);i>0;i-=lowbit(i))
{
//
int t=hashtable[lowbit(i)];
row[mx]-=1 << t-1;
col[my]-=1 << t-1;
cell[mx/3][my/3]-=1 << t-1;
str[mx*N+my]='0'+t;
if(dfs(cnt-1)==true) return true;
row[mx]+=1 << t-1;
col[my]+=1 << t-1;
cell[mx/3][my/3]+=1 << t-1;
str[mx*N+my]='.';
}
return false;
}
int main(){
for(int i = 0 ;i < N ;i++) hashtable[1<<i]=i+1; // 1-> 1 10-> 2...
for(int i=0;i<1<<N;i++)
{
int s=0;
for(int j=i;j>0;j-=lowbit(j)) s++;
ones[i]=s;
}
while(cin >> str,str[0]!='e')
{
init();
int cnt=0;
for(int i=0,k=0;i<N;i++)
{
for(int j=0;j<N;j++,k++)
{
if(str[k] != '.')
{
int t=str[k]-'0';
row[i]-=1<<(t-1);
col[j]-=1<<(t-1);
cell[i/3][j/3]-=1<<(t-1);
}
else
cnt++;
}
}
dfs(cnt); //cnt表示有多少格子要填;
cout << str << endl;
}
return 0;
}