dfs 实践:数独

数独

数独是一种传统益智游戏,你需要把一个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
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 9;

char str[100];
// ones 记录当前二进制数有几个 1     map 返回当前二进制数结尾 1 的位置
int ones[1 << N], map[1 << N];
// 记录 col 行  row 列  cell 九宫格 可选项
int col[N], row[N], cell[3][3];

// 返回二进制数中最后一个 1 坐在的位置
inline int lowbit(int x) {
    return x & -x;
}

// inline 可以减少调用函数的时间,但对递归函数无效 
inline void init() {
    // 将每个位置都初始化为可放置数字  1000000000 - 1 = 11111111 
    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;
    }
    
}

// 返回当前格子可选择的数的个数
inline 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 minv = 10;
    int x, y;
    // 寻找可选择数量最少的格子
    for (int i = 0; i < N; i ++) {
        for (int j = 0; j < N; j ++) {
            if (str[i * N + j] == '.') {
                int t = ones[get(i, j)];
                if (t < minv) {
                    minv = t;
                    x = i, y = j;
                }
            }
        }
    }
    
    // 遍历当前可选的状态
    for (int i = get(x, y); i; i -= lowbit(i)) {
        int t = map[lowbit(i)];
        
        // 填充
        // 将当前数代表的状态置 0
        row[x] -= 1 << t;
        col[y] -= 1 << t;
        cell[x / 3][y / 3] -= 1 << t;
        // 填充格子
        str[x * N + y] = t + '1';
        
        if (dfs(cnt - 1)) return true;
        
        // 恢复现场
        // 将当前数代表的状态置 1
        row[x] += 1 << t;
        col[y] += 1 << t;
        cell[x / 3][y / 3] += 1 << t;
        // 将格子恢复原状
        str[x * N + y] = '.';
    }
    
    return false;
}

int main() {
    for (int i = 0; i < N; i ++) map[1 << i] = i;
    for (int i = 0; i < 1 << N; i ++) {
        int s = 0;
        // 100010 = 1000 +  10
        for (int j = i; j; j -= lowbit(j)) s ++;
        ones[i] = s;
    }
    
    while (scanf("%s", str), strcmp(str, "end")) {
        init();
        
        // 记录需要填充的位置数量
        int cnt = 0;
        for (int i = 0, k = 0; i < N; i ++) {
            for (int j = 0; j < N; j ++, k ++) {
                // 存放的是 0 - 8
                // 通过已填放数,来更新行 列 九宫格的状态
                if (str[i * N + j] != '.') {
                    int t = str[i * N + j] - '1';
                    if (t != '.') {
                        col[j] -= 1 << t;
                        row[i] -= 1 << t;
                        cell[i / 3][j / 3] -= 1 << t;
                    }
                }else cnt ++;
            }
        }
        
        dfs(cnt);
        
        cout << str << endl;
    }
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值