搜索_DFS_POJ3096_Sudoku

版权声明:本文为博主原创作品, 转载请注明出处! 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;
} 

      

猜你喜欢

转载自blog.csdn.net/solider98/article/details/84126457