题目链接:https://www.acwing.com/problem/content/168/
题目:
数独是一种传统益智游戏,你需要把一个 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
分析:
此题的话思路上就是,找到空缺的位置,然后对其这一行,这一列,周围圈出的9个值,看能上哪些数,然后将这些数,放入,看是否满足答案。
但是,这其中存在一些优化。
首先,可以对搜索顺序进行优化,我们每次都搜索可以选择的数少的情况,这样就减少了分支。
而对于可以选择哪些数进行搜索,这里可以使用二进制的方式进行优化。
9位二进制数,111111111。
从右往左,第0位取1,则可以使用1. 第1位取1,则可以使用2,第2位取1则可以使用3.
以此类推。如果为0,则说明这个在某行或者某列,或9格中已经用到了,不能用。
而哪一位为1可以直接for()循环找,同样也可以使用lowbit()的方式找哪个位置为1.
举例:
n = 100100, 则 lowbit(n) = 100, ----> 1 << 2, 所以二进制中第2位(从0开始)为1.
n -= lowbit(n) ---> n == 1000000,以此类推。
而哪一位唯一,可以先进行预处理。 做一个lowbit()值 与 第几位为1的映射.
而从0~ 1 << 9 - 1 每个二进制表示的值中有多少个1,也可以先预处理出来。
代码实现:
# include <iostream>
using namespace std;
const int N = 10 , M = 1 << N;
int row[N],lie[N],ceil[3][3];
int num_1[M]; // num_1[i],当前i这个值1的个数
int map[M] ; // 由于lowbit(i)返回的是,2 ^ k 的值,而我们需要k,也就是第几个位置为1.所以预处理一个映射,2 ^ k ----> 就是 k , 也就是 map[2 ^ k] == k
char ch[100];
//第0个位置,则上1 , 第1个位置,则上2, 第二个位置,则上3
int num[] = {1,2,3,4,5,6,7,8,9};
void init()
{
for(int i = 0 ; i < 9 ; i++) // 直觉上感觉每次将一个一维变二维的时候,都会从下标为0开始,而不是下标为1开始
{
row[i] = lie[i] = ( 1 << 9 ) - 1;
}
for(int i = 0 ; i < 3 ; i++)
{
for(int j = 0 ; j < 3 ; j++)
{
ceil[i][j] = (1 << 9) - 1;
}
}
}
int lowbit(int x)
{
return x & -x;
}
void draw(int x , int y , int t , bool state) // 根据state的值,true则在[x][y]这个位置放上t这个值,false则删去这个值
{
if(state)
{
ch[x * 9 + y] = '0' + t;
}
else
{
ch[x * 9 + y] = '.'; // 删除的话就变回原样
}
int v = 1 << ( t - 1 ) ; // 第t - 1号位置为1,
if(state)
{
row[x] -= v;
lie[y] -= v;
ceil[x / 3][y / 3] -= v;
}
else
{
row[x] += v;
lie[y] += v;
ceil[x / 3][y / 3] += v;
}
}
int get(int x , int y)
{
return row[x] & lie[y] & ceil[x / 3][y / 3];
}
bool dfs(int cnt)
{
if(cnt == 0)
{
return true;
}
int temp = 10;
int x,y;
for(int i = 0 ; i < 9 ; i ++) // 去找1最少的情况,也就是分支最少的情况
{
for(int j = 0 ; j < 9 ; j++)
{
if(ch[i * 9 + j] == '.')
{
if( num_1[ get(i,j) ] < temp )
{
temp = num_1[ get(i,j) ] ;
x = i;
y = j;
}
}
}
} // 获得了x,y下1最少的情况
int choose_num = get(x,y); // x,y可以取哪些值
for(int i = choose_num ; i ; i -= lowbit(i))
{
int tt = lowbit(i);
draw(x , y , num[map[tt] ] , true);
if(dfs(cnt - 1))
{
return true;
}
draw(x , y , num[map[tt] ] , false);
}
return false;
}
int main()
{
for(int i = 0 ; i < 1 << 9 ; i++)
{
int cnt = 0 ;
for(int j = 0 ; j < 9 ; j++)
{
if(i >> j & 1)
{
cnt++;
}
}
num_1[i] = cnt;
}
for(int i = 0 ; i < 9 ; i++)
{
map[1 << i] = i; // 1 << i就是我们后面会用到的lowbit()得到的值, 使用map[]对应一个我们真正需要的i,也就是第几个位置为1
}
while(cin >> ch)
{
if(ch[0] == 'e')
{
break;
}
init();
int cnt = 0; // 统计空余出来需要填数值的个数
for(int i = 0 ; i < 9 ; i++)
{
for(int j = 0 ; j < 9 ; j++)
{
if(ch[i * 9 + j] != '.')
{
draw(i , j , ch[i * 9 + j] - '0' , true);
}
else
{
cnt++;
}
}
}
dfs(cnt);
puts(ch);
}
return 0;
}