靶形数独
思路
搜索顺序:对于每个空白位置,枚举可以填的数
剪枝优化:
- 优先选择可选数字最少的空白的位置搜索
- 使用二进制来存储每一行可选数字,每一列可选数字和每一个九宫格内可选数字,通过一步与操作便可得到每一个空白处可选数。
- 可以预处理出每一个从
0
到1 << N
所有数包含1
的个数和log
函数,减小常数
code
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 9, INF = 0x3f3f3f3f;
int g[N][N];
int row[N], col[N], cell[3][3];
int ones[1<<N], map[1<<N];
int ans;
inline int lowbit(int x)
{
return x & -x;
}
inline int mmin(int a, int b, int c, int d)
{
return min(min(a, b), min(c, d));
}
inline int get(int x, int y)
{
return row[x] & col[y] & cell[x/3][y/3];
}
inline int check()
{
int res = 0;
for (int i = 0; i < N; i ++)
for (int j = 0; j < N; j ++)
{
int t = mmin(abs(i), abs(i-8), abs(j), abs(j-8));
res += g[i][j] * (t + 6);
}
return res;
}
void dfs(int cnt)
{
if(!cnt)
{
ans = max(ans, check()); // 更新最大值
return;
}
int x, y, min = INF;
for (int i = 0; i < N; i ++) // 优先选择待选数最少的空白位置,减小搜索分支
for (int j = 0; j < N; j ++)
{
int t = g[i][j];
if(!t)
{
int k = get(i, j);
if(ones[k] < min)
{
min = ones[k];
x = i;
y = j;
}
}
}
for (int i = get(x, y); i; i -= lowbit(i)) // 尝试每一个待选数
{
int t = map[lowbit(i)];
row[x] -= 1 << t;
col[y] -= 1 << t;
cell[x/3][y/3] -= 1 << t;
g[x][y] = t + 1;
dfs(cnt-1);
g[x][y] = 0; // 记得恢复现场
cell[x/3][y/3] += 1 << t;
col[y] += 1 << t;
row[x] += 1 << t;
}
}
int main()
{
for (int i = 0; i < N; i ++) map[1 << i] = i;
for (int i = 0; i < (1 << N); i ++)
{
for (int j = i; j; j -= lowbit(i)) ones[i] ++;
}
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;
for (int i = 0; i < N; i ++)
for (int j = 0; j < N; j ++)
scanf("%d", &g[i][j]);
int cnt = 0;
for (int i = 0; i < N; i ++)
for (int j = 0; j < N; j ++)
{
int t = g[i][j];
if(t)
{
t --;
row[i] -= 1 << t;
col[j] -= 1 << t;
cell[i/3][j/3] -= 1 << t;
}
else cnt ++;
}
dfs(cnt);
if(!ans) puts("-1");
else printf("%d\n", ans);
return 0;
}
时间复杂度
最坏情况下需要枚举81个空格,每个空格有9种选择,最坏情况下计算量为 9 81 9^{81} 981
由于剪枝存在,实际的搜索空间远小于最坏情况