AcWing 166.数独

使用的优化:

  • 优化搜索顺序:
    每次选择分支最少的格子进行搜索
  • 可行性优化:
    每行每列每个九宫格不能有重复数字
  • 二进制优化:
    每行每列每个九宫格使用二进制数来表示每个数是否能够被使用,如果是1那么就能被使用,0就不能被使用,这样可以通过对行列九宫格进行与运算和lowbit运算可以快速的求出能使用哪些数
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const int N = 9, M = 1 << 9;

int ones[M], map[M]; // ones为了快速求出来每个二进制数有多少个1
int row[N], col[N], cell[3][3];
char str[100];

int lowbit(int x)
{
    return x & -x;
}

void init()
{
    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;
}

void draw(int x, int y, int t, bool is_set) 
// 传入一个is_set参数判断是插入数字还是删除数字, 在第x行y列加上t或者删除t
{
    if (is_set) str[x * N + y] = t + '1';
    else str[x * N + y] = '.';

    int v = 1 << t;
    if (!is_set) v = -v; // 如果是插入那么就是减去这个二进制数,删除就是加上这个二进制数

    row[x] -= v;
    col[y] -= v;
    cell[x / 3][y / 3] -= v;
}

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 x, y;
    int minv = 20;
    for (int i = 0; i < N; i ++ )
        for (int j = 0; j < N; j ++ )  
            if (str[i * N + j] == '.')
            {
                int state = get(i, j);
                if (ones[state] < minv) 
                {
                    minv = ones[state];
                    x = i, y = j;
                }
            }

    int state = get(x, y);
    for (int i = state; i; i -= lowbit(i))
    {
        int t = map[lowbit(i)];
        draw(x, y, t, true);
        if (dfs(cnt - 1)) return true;
        draw(x, y, t, false);
    }
    
    return false;
}

int main() 
{
    for (int i = 0; i < N; i ++ ) map[1 << i] = i; // 以2为底的log为几, 用于判断lowbit之后在那个位置操作
    for (int i = 0; i < 1 << N; i ++ ) // 预处理每个二进制数有多少个1
        for (int j = 0; j < N; j ++ ) 
            ones[i] += i >> j & 1;

    while (cin >> str, str[0] != 'e')
    {
        init();

        int cnt = 0;
        for (int i = 0, k = 0; i < N; i ++ )
            for (int j = 0; j < N; j ++ , k ++ ) 
                if (str[k] != '.') 
                {
                    int t = str[k] - '1';
                    draw(i, j, t, true);
                }
                else cnt ++ ;

        dfs(cnt);

        puts(str);
    }

    return 0;
}
posted @ 2021-03-10 01:43  筱翼深凉  阅读(101)  评论(1编辑  收藏  举报