版权声明:本文为博主原创作品, 转载请注明出处! https://blog.csdn.net/solider98/article/details/84126457
思路分析:
考虑使用DFS, 使用如下剪枝策略:
(1)检查所有空格, 如果有空格无法填任何数, 立即回溯, 如果有空格只能填唯一的数, 立即填上该数
(2)检查所有行, 如果存在某个数不能填在任何一行, 立即回溯, 如果某个数能填在该行唯一空格处, 立即填上该数.
(3)检查所有列, 如果存在某个数不能填在任何一列, 立即回溯, 如果某个数能填在该列唯一空格处, 立即填上该数
(4)检查所有16宫格, 重复与(2)(3)类似的过程
(5)选择当前可选方案填数方案最少的空格填数, 然后递归
下面给出基于上述剪枝策略的AC代码:
//POJ3076_Sudoku
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define fi first
#define se second
using namespace std;
//G:格局,P:每个空格可填的数, cnt: 当前非空格个数, ok: 1表示已经输出结果, 0为未输出
int G[17][17], P[17][17], cnt, ok;
int ah[17][17], bh[1 << 16 + 1], ph[37];//G[i][j]所在4 * 4网格,bh:哈希lowbit的值,ph:哈希lg(i)
pair<int, int> pos[17];//pos[i].first网格i左上角所在行, second:所在列
char cstr[20];
int lowbit(int a){
int ans = 0; while(a) a -= a & -a, ++ans; return ans;
}
//在G[x][y]填上数字num
void fill(int x, int y, int num){
G[x][y] = num, ++cnt;
for(int i = 1; i <= 16; ++i) P[x][i] &= ~(1 << num - 1), P[i][y] &= ~(1 << num - 1);
for(int k = ah[x][y], p = pos[k].fi; p <= pos[k].fi + 3; ++p)
for(int q = pos[k].se; q <= pos[k].se + 3; ++q) P[p][q] &= ~(1 << num - 1);
}
void dfs(){
for(int i = 1; i <= 16; ++i)
for(int j = 1; j <= 16; ++j)
if(!G[i][j])
if(!bh[P[i][j]]) return;
else if(bh[P[i][j]] == 1) fill(i, j, ph[P[i][j] % 37] + 1);
//检查所有行是否有唯一数字可填
for(int i = 1; i <= 16; ++i)
for(int num = 1; num <= 16; ++num){
int t = 0, e, f = 0;
for(int k = 1; t <= 1 && k <= 16; ++k){
if(G[i][k] == num){
f = 1; break;
}
if(G[i][k]) continue;
if(P[i][k] >> num - 1 & 1) ++t, e = k;
}
if(f) continue;
if(!t) return;
if(t == 1) fill(i, e, num);
}
//检查每一列是否有唯一数字可填
for(int i = 1; i <= 16; ++i)
for(int num = 1; num <= 16; ++num){
int t = 0, e, f = 0;
for(int k = 1; k <= 16 && t <= 1; ++k){
if(G[k][i] == num){
f = 1; break;
}
if(G[k][i]) continue;
if(P[k][i] >> num - 1 & 1) ++t, e = k;
}
if(f) continue;
if(!t) return;
if(t == 1) fill(e, i, num);
}
//检查每一个16宫格是否有唯一数字可填
for(int i = 1; i <= 16; ++i)
for(int num = 1; num <= 16; ++num){
int t = 0, ex, ey, f = 0;
for(int p = pos[i].fi; t <= 1 && !f && p <= pos[i].fi + 3; ++p)
for(int q = pos[i].se; q <= pos[i].se + 3; ++q){
if(G[p][q] == num){
f = 1; break;
}
if(G[p][q]) continue;
if(P[p][q] >> num - 1 & 1) ++t, ex = p, ey = q;
}
if(f) continue;
if(!t) return;
if(t == 1) fill(ex, ey, num);
}
if(cnt == 16 * 16){
ok = 1; return;
}
int arr[17][17]; memcpy(arr, P, sizeof(int) * 17 * 17);
int brr[17][17]; memcpy(brr, G, sizeof(int) * 17 * 17);
int cntt = cnt;
//寻找候选方案最少的空格填数
int mx, my, tmp = 17;
for(int i = 1; i <= 16; ++i)
for(int j = 1; j <= 16; ++j)
if(!G[i][j] && bh[P[i][j]] < tmp) tmp = bh[P[i][j]], mx = i, my = j;
for(int i = 1; i <= 16; ++i)
if(P[mx][my] >> i - 1 & 1){
fill(mx, my, i), dfs();
if(ok) return;
else
memcpy(P, arr, sizeof(int) * 17 * 17)
, memcpy(G, brr, sizeof(int) * 17 * 17), cnt = cntt;
}
}
int main(){
for(int i = 1, j = 0; j <= 17; ++j, i <<= 1) ph[i % 37] = j;
for(int i = 1; i < 1 << 16; ++i) bh[i] = lowbit(i);
for(int i = 1; i <= 4; ++i)
for(int j = 1; j <= 4; ++j)
pos[4 * (i - 1) + j].fi = 4 * (i - 1) + 1, pos[4 * (i - 1) + j].se = 4 * (j - 1) + 1;
for(int i = 1; i <= 16; ++i)
for(int j = 1; j <= 16; ++j) ah[i][j] = 4 * (i / 4 - !(i % 4)) + j / 4 + 1 - !(j % 4);
while(true){
memset(G, 0, sizeof(G)), cnt = 0, ok = 0;
for(int i = 1; i <= 16; ++i)
for(int j = 1; j <= 16; ++j) P[i][j] = (1 << 16) - 1;
for(int i = 1; i <= 16; ++i){
if(scanf("%s", cstr + 1) != 1) return 0;
for(int j = 1; j <= 16; ++j){
int num = cstr[j] == '-'? 0: cstr[j] - 'A' + 1;
if(num) fill(i, j, num);
}
}
dfs();
for(int i = 1; i <= 16; ++i, cout << endl)
for(int j = 1; j <= 16; ++j) cout << (char)(G[i][j] + 'A' - 1);
cout << endl;
}
return 0;
}