题目描述
数独 是一种传统益智游戏,你需要把一个 9×9 的数独补充完整,使得数独中每行、每列、每个 3×3 的九宫格内数字 1∼9 均恰好出现一次。
请编写一个程序填写数独。
输入
输入包含多组测试用例。
每个测试用例占一行,包含 81个字符,代表数独的 81 个格内数据(顺序总体由上到下,同行由左到右)。
每个字符都是一个数字(1−9)或一个 .(表示尚未填充)。
您可以假设输入中的每个谜题都只有一个解决方案。
文件结尾处为包含单词 end 的单行,表示输入结束。
输出
每个测试用例,输出一行数据,代表填充完全后的数独。
样例输入
4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4...... ......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3. end
样例输出
417369825632158947958724316825437169791586432346912758289643571573291684164875293 416837529982465371735129468571298643293746185864351297647913852359682714128574936
#include <iostream>
#include <cstring>
using namespace std;
const int N = 9,M = 1 << N;
int ones[M],map[M];
//ones:记录一个状态值中的1的个数,例:100010000,1的个数为2
//map:记录㏒n为多少(以2为底),例:㏒(2^3) = ㏒8 = 3;map[㏒8] = 3
int row[N],col[N],cel[N / 3][N / 3];
char str[100];
void init()//初始化row[]和col[]数组
{
for(int i = 0;i < N;i++) row[i] = col[i] = (1 << N) - 1;//初始时全都是未选状态,所以应该是111111111(9个1)
//2^9 - 1(十进制) = 111111111(二进制)
for(int i = 0;i < 3;i++)
for(int j = 0;j < 3;j++)
cel[i][j] = (1 << N) - 1;
}
int lowbit(int x)//返回二进制中的最后一个"1",返回的形式是2的几次幂
{
return x & -x;
}
void draw(int x,int y,int t,bool is_set)//在(x,y)根据is_set不同进行填充或删除t
{
//对于str的操作,用来输入时进行初始化
if(is_set) str[x * N + y] = '1' + t;//t的值是0-8,加上'1'变成0-9,如果is_set为true,进行添加操作
else str[x * N + y] = '.';//否则该位置变为'.',意味着空格。
//对于row,col,cel的操作
int v = 1 << t;
if(!is_set) v = -v;
/*
由于空格为1,已填充的是0
所以填充空格时,1->0,所以要减去v
将一个位置上的数删除变为空格时,0->1,v取负
举个5位数的例子:
(1)10100(01234)
填充2这个位置,把1变成0,则要减去2的2次方为100
10100
- 100
-------
10000
(2)10000(01234)
将2这个位置删除,把0变成1,先把v = (1 << 2) 取反为:1000 0100
0001 0000
-1000 0100
-----------
0001 0100
*/
row[x] -= v;
col[y] -= v;
cel[x / 3][y / 3] -= v;
}
int get(int x,int y)
{
return row[x] & col[y] & cel[x / 3][y / 3];//取三个状态值的交集(按位与)
}
bool dfs(int cnt)
{
if(!cnt) return true;;
//优化搜索顺序
int x,y;
int minv = 10;
for(int i = 0;i < N;i++)
{
for(int j = 0;j < N;j++)
{
if(str[i * N + j] == '.')
{
int state = get(i,j);//枚举所有合法状态
if(ones[state] < minv)//寻找状态中空位最少的状态
{
minv = ones[state];
x = i;//记录空位最少的状态的下标
y = j;
}
}
}
}
int state = get(x,y);
for(int i = state;i;i -= lowbit(i))//枚举空位
{
int t = map[lowbit(i)];//t为这个空位应填什么值
draw(x,y,t,true);
if(dfs(cnt - 1)) return true;
draw(x,y,t,false);
}
return false;
}
int main()
{
for(int i = 0;i < N;i++) map[1 << i] = i;//map初始化;例:map[㏒8] = 3
//处理ones数组,表示所有可能的状态值中,每个状态值中1的个数
for(int i = 0;i < 1 << N;i++)//0-511种状态值
{
for(int j = 0;j < N;j++)//每种状态值有9位
{
ones[i] += (i >> j & 1);//每一位中如果为1,则计数
}
}
while(cin>>str,str[0] != 'e')//以end结尾
{
init();//初始化row,col,cel
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] - '1';//(0-9)->(0-8)
draw(i,j,t,true);
}
else cnt++;
}
}
dfs(cnt);
puts(str);
}
return 0;
}