166. 数独(深搜+剪枝)

166. 数独

AcWing
来源:166. 数独
深搜+剪枝

166. 数独(深搜+剪枝)_第1张图片166. 数独(深搜+剪枝)_第2张图片

思路

9 * 9的格子,一个格子九种填法,暴搜肯定TLE

考虑优化

1、选择优化,由于肯定有解,我们先搜索可选条件少的点

2、其次考虑如何快速得到我们选择的点还可以选哪几个,我们知道的限制有行上、列上、单个3 * 3九宫格内,我们能选的就是这几个的交集,为了快速得到,就需要用位运算优化

我们建立ROW数组存储第i列可选的数,COL存储第i列可选的数,cell存储第i,j个九格宫可以存储的数,用二进制存储状态
166. 数独(深搜+剪枝)_第3张图片
比如第1行,2、7、3、8、1都不可以选择了,那么这一行的状态用二进制表示就是100111001,0代表还未使用,1表示已经使用

那么对于第i,j个点,我们可以选的二进制状态就是 r[i] & c[j] & cell[i][j];

为了快速得到某个二进制数中的1,就用到了lowbit函数,求一个二进制最低位的1,因为我们得到这个1以后也是一个二进制数,为了快速得到他代表数字几,我们要需要打表map记录每种二进制代表哪个数字

我们说了要优先选择可选少的,那么为了快速得到某个点的可选数,我们还需要打表ones数组,记录每个二进制中有几个1

#include 
#include 
#include 
using namespace std;

const int N = 9;
int ones[1 << N], map[1 << N];
int r[N], c[N], cell[3][3];
string str;

inline int lowbit(int x) {return x & -x;}//lowbit函数求二进制最低位1

inline void init() {//初始化
    for(int i = 0;i < N;i++) r[i] = c[i] = (1 << N) - 1;
    //(1 << N) - 1 = (二进制)111111111
    //表示刚开始,每个数都可以选
    
    for(int i = 0;i < 3;i++)
        for(int j = 0;j < 3;j++)
            cell[i][j] = (1 << N) - 1;//刚开始每个九格宫内9个数都可以选
    
}

inline int get(int x, int y) {return r[x] & c[y] & cell[x / 3][y / 3];}//得到x,y这个点可选的状态

bool dfs(int cnt){
    if(!cnt) return true;
    
    int minn = 10, x = 0, y = 0;
    for(int i = 0;i < N;i++){//找到可选最少的那个点
        for(int j = 0;j < N;j++){
            if(str[i * 9 + j] == '.'){
                int t = ones[get(i, j)];
                if(t < minn){
                    minn = t, x = i, y = j;
                }
            }
        }
    }
    // cout << x << " " << y << endl; 
    // return false;
    for(int i = get(x, y);i;i -= lowbit(i)){//枚举这个点上可选的数
        int t = map[lowbit(i)];
        r[x] -= 1 << t;
        c[y] -= 1 << t;
        cell[x / 3][y / 3] -= 1 << t;
        str[x * 9 + y] = t + '1';
        if(dfs(cnt - 1)) return true;
        r[x] += 1 << t;//回溯
        c[y] += 1 << t;//回溯
        cell[x / 3][y / 3] += 1 << t;//回溯
        str[x * 9 + 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;
        for(int j = 0;j < N;j++) {
            if(i >> j & 1) s++;
        }
        ones[i] = s;//打表
    }
    
    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] != '.'){//如果不是空格,就是有数,我们把行、列、九格宫状态更新一下
                    r[i] -= 1 << (str[k] - '1');
                    c[j] -= 1 << (str[k] - '1');
                    cell[i / 3][j / 3] -= 1 << (str[k] - '1');
                }
                else cnt++;
            }
        }
        
        dfs(cnt);
        
        cout << str << endl;
        
    }
    
    return 0;
}

你可能感兴趣的:(AcWing)