AcWing 166.数独
使用的优化:
- 优化搜索顺序:
每次选择分支最少的格子进行搜索 - 可行性优化:
每行每列每个九宫格不能有重复数字 二进制优化:
每行每列每个九宫格使用二进制数来表示每个数是否能够被使用,如果是1那么就能被使用,0就不能被使用,这样可以通过对行列九宫格进行与运算和lowbit运算可以快速的求出能使用哪些数
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 9, M = 1 << 9;
int ones[M], map[M]; // ones为了快速求出来每个二进制数有多少个1
int row[N], col[N], cell[3][3];
char str[100];
int lowbit(int x)
{
return x & -x;
}
void init()
{
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;
}
void draw(int x, int y, int t, bool is_set)
// 传入一个is_set参数判断是插入数字还是删除数字, 在第x行y列加上t或者删除t
{
if (is_set) str[x * N + y] = t + '1';
else str[x * N + y] = '.';
int v = 1 << t;
if (!is_set) v = -v; // 如果是插入那么就是减去这个二进制数,删除就是加上这个二进制数
row[x] -= v;
col[y] -= v;
cell[x / 3][y / 3] -= v;
}
int get(int x, int y)
{
return row[x] & col[y] & cell[x / 3][y / 3];
}
bool dfs(int cnt)
{
if (!cnt) return true;
int x, y;
int minv = 20;
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)];
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; // 以2为底的log为几, 用于判断lowbit之后在那个位置操作
for (int i = 0; i < 1 << N; i ++ ) // 预处理每个二进制数有多少个1
for (int j = 0; j < N; j ++ )
ones[i] += i >> j & 1;
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] - '1';
draw(i, j, t, true);
}
else cnt ++ ;
dfs(cnt);
puts(str);
}
return 0;
}