AcWing166.数独(dfs+剪枝)

题目: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位都是1,使用的之后的那位变为0,然后,每次先用row[x] & col[y] & cell[x/3][y/3]找到那个点的限制的九位数,利用lowbit运算取出可以用的数,然后我们考虑优化搜索顺序,每次找到可选数字最少的点进行填充(这样可以使搜索分支变少)。

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,b,a) for(int i=b;i>=a;i--)
#define Mst(a,b) memset(a,b,sizeof(a))
#define lowbit(x) x&-x
#define x first
#define y second
const int N = 9;
typedef pair<int,int> PII;
string str;
int row[9],col[9],cell[3][3],ones[1<<10];
unordered_map<int,int> mp;
void init() {
    rep(i,0,8) {
        row[i] = (1<<N)-1;
        col[i] = (1<<N)-1;
    }
    
    rep(i,0,2) {
        rep(j,0,2) {
            cell[i][j] = (1<<N)-1;
        }
    }
}
int get(int x,int y) {
	return row[x] & col[y] & cell[x/3][y/3];
}
void dfs(int cnt) {
    if(!cnt) {
    	cout << str << endl;
    	return;
	}
	
	int minn = 10,x,y;
	rep(i,0,N-1) {
		rep(j,0,N-1) {
			if(str[i * N + j] != '.') continue;
			int state = get(i,j);
			if(minn > ones[state]) {
				minn = ones[state];
				x = i; y = j;
			}
		}
	}
	
	int state = get(x,y);
	
	for(int i = state;i;i -= lowbit(i)) {
		  int d = lowbit(i);
		  int t = mp[lowbit(i)];
		  row[x] -= d;
		  col[y] -= d;
		  cell[x/3][y/3] -= d;
		  str[x * N + y] = char('1' + t);
		  dfs(cnt-1);
		  str[x * N + y] = '.';
		  row[x] += d;
		  col[y] += d;
		  cell[x/3][y/3] += d;	
	}
}
int main() {
    
       ios::sync_with_stdio(false);
         cin.tie(0);
       
	    rep(i,0,8) mp[1<<i] = i;
	    
        rep(i,1,1<<N) {
		  rep(j,0,N-1) {
		  	  ones[i] += (i >> j) & 1;
		  } 
		} 
    while(cin >> str && str != "end") {
         init();
         int len = str.size();
         
         int cnt = 0;
         rep(i,0,len-1) {
         	 if(str[i] == '.') {
         	 	  cnt++;continue;
			  }
             int x = i/9,y = i%9,c = str[i] - '1';
             row[x] -= (1<<c);
             col[y] -= (1<<c);
             cell[x/3][y/3] -= (1<<c);
         }
        
        dfs(cnt);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值