数独的填充——AcWing 166. 数独

#include <bits/stdc++.h>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <unordered_map>
#define xx first
#define yy second
using namespace std;
typedef pair<int, int> PII;
typedef pair<int, PII> PIII;
typedef long long ll;
const int maxn = 9, maxm = 1 << maxn;
int ones[maxm], ma[maxm];
int row[maxn], col[maxn], cell[3][3];
char str[100];
void init() {
    for (int i = 0; i < maxn; i++)
        row[i] = col[i] =
            (1 << maxn) -
            1;  //是这些限制里面存的每一位都是1,也就是初始化为0~8为都是1,都可以用。
    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++) cell[i][j] = (1 << maxn) - 1;
    return;
}
void draw(
    int x, int y, int t,
    bool
        is_set)  //这里is_set是用来判端是否写入还是擦除:t为该点被选中时,是填入的或者删除的那个数字。
{
    if (is_set)
        str[x * maxn + y] = '1' + t;  //一行九个数字。
    else
        str[x * maxn + y] = '.';

    int v = 1 << t;
    if (!is_set) v = -v;
    row[x] -= v;  //将这个可选项减下去,v为负时则为把这个t的可选项加上。
    col[y] -= v;
    cell[x / 3][y / 3] -= v;
}
int lowbit(int x) { return x & -x; }
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;  //每一个格子最多有九种可选项,所以minv取10为初值。
    int x, y;
    for (int i = 0; i < maxn; i++) {
        for (int j = 0; j < maxn; j++) {
            if (str[i * maxn + j] == '.') {
                int state = get(
                    i,
                    j);  // state仅仅知识返回一个可以用的01串,ones可以直接得到这个01串中有多少个1,也就是可以选择多少中数字。
                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=ma[lowbit(i)];
    //     draw(x,y,t,true);
    //     if(dfs(cnt-1))return true;//如果可以则不需要其他路径继续走啦,
    //     直接一路返回true不跑剩余分支。 draw(x,y,t,false);
    // }
    while (state) {
        int t = ma[lowbit(state)];
        draw(x, y, t, true);
        if (dfs(cnt - 1)) return true;
        draw(x, y, t, false);
        state -= lowbit(state);
    }
    return false;
}
int main() {
    for (int i = 0; i < maxn; i++)
        ma[1 << i] =
            i;  //用于接下来直接用这个lowbit数字找到这是2的多少次方等于这个数。
    for (int i = 0; i < 1 << maxn; i++) {
        for (int j = 0; j < maxn; j++) {
            ones[i] += i >> j & 1;
        }
    }
    while (cin >> str, str[0] != 'e')  //文件以end结束。
    {
        init();
        int cnt = 0;
        for (int i = 0, k = 0; i < maxn; i++) {
            for (int j = 0; j < maxn; j++, k++) {
                if (str[k] != '.') {
                    int t = str[k] - '1';
                    draw(i, j, t, true);
                } else
                    cnt++;  //需要递归多少层,draw(1)多少个节点后才能算dfs结束的条件。
            }
        }
        dfs(cnt);
        puts(str);
    }

    return 0;
}

记得之前打过一次美团比赛也是一个数独的题目,图要比一般的图要大,直接输进去答案的题目,当时一脸懵逼,还好大圣厉害给做啦出来减啦20分钟罚时。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值