166.数独

发布时间 2023-11-12 00:01:41作者: Gold_stein
#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <string>
using namespace std;

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

char str[100];
int Row[N], Line[N], Block[3][3]; // 哪些数字可以填, 1可以填写, 0 不能填写
int Log[M], cnt[M], Empty;

void Deal(int x, int y, int num, int f)
{
    if (f < 0)
        str[x * 9 + y] = num + '1';
    else
        str[x * 9 + y] = '.';
    int Target = (1 << num) * f;
    Row[x] += Target;
    Line[y] += Target;
    Block[x / 3][y / 3] += Target;
}

int query(int x, int y)
{
    int tmp = Row[x] & Line[y] & Block[x / 3][y / 3];
    return cnt[tmp];
}

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

bool dfs(int Done)
{
    if (Done == 0)
    {
        return true;
    }
    int tx = -1, ty = -1, m = 0x3f3f3f3f;
    for (int i = 0; i < 9; i++)
        for (int j = 0; j < 9; j++)
        {
            if(str[i * 9 + j] != '.') continue;
            int tmp = query(i, j);
            if (tmp < m)
            {
                m = tmp;
                tx = i;
                ty = j;
            }
        }
    int State = Row[tx] & Line[ty] & Block[tx / 3][ty / 3];
    for(int i = State; i; i -= lowbit(i))
    {
        int tmp = lowbit(i);
        int Pos = Log[tmp];
        Deal(tx, ty, Pos, -1);
        if(dfs(Done - 1)) return true;
        Deal(tx, ty, Pos, 1);
    }
    return false;
}

void init()
{
    for (int i = 0; i < N; i++)
        Row[i] = Line[i] = (1 << N) - 1;
    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++)
            Block[i][j] = (1 << N) - 1;
}

int main()
{
    for (int i = 0; i < N; i++)
        Log[1 << i] = i;
    for (int i = 0; i < M; i++)
        for (int j = 0; j < N; j++)
            cnt[i] += i >> j & 1;
    while (cin >> str && str[0] != 'e')
    {
        init();
        Empty = 0;
        for (int i = 0, Now = 0; i < 9; i++)
            for (int j = 0; j < 9; j++, Now++)
            {
                if (str[Now] != '.')
                    Deal(i, j, str[Now] - '1', -1);
                else
                    Empty++;
            }
        dfs(Empty);
        puts(str);
    }
    return 0;
}

写的时候太马虎大意了,dfs忘记加最重要的判断语句:

if(str[i * 9 + j] != '.') continue;

导致一直搜不出来

一开始想用每一位数的0表示没填过(即还可以填)  1反之

但写到一半才意识到这种写法是不可取的

因为如果这么写的话,为了配合lowbit运算,我们就需要对得到的结果数字进行取反。

但是我们的cnt数组只处理了0~1 << N 范围内的数字,取反之后会超出这个范围,导致计算出错,甚至产生segment fault

 

这道题还有个值得注意的点:

因为这道题不是找最优解或者统计方案数,所以不能写成void(void形式最终会找到所有的可行解,可能会TLE)

这道题需要写成bool形式,一旦当前搜索树的一棵子树找到了答案,就没有必要再继续搜索它的其他子树了,因为我们只需要一个可行解即可。