《算法竞赛进阶指南》 0x22深度优先搜索 数独填数问题

题目链接:http://poj.org/problem?id=3074

本题的重点在于利用二进制位高效地判断一个位置能填哪些数,行、列、九宫格中能填的数的用一个二进制数表示,相与便是能填的数的二进制表示,使用lowbit函数可以

依次取出最低有效位进行搜索。基于几个常识:我们做数独的时候一定每次都是从可能性最大的点入手的,也就是行列和九宫格中都不冲突的数越少越好,所以可以每次搜索都将这样的位置找出来

进行处理,大大降低了搜索树的分支数量。

代码:

#include<iostream>
#include<cstdio>
using namespace std;
char str[10][10];
int row[9],col[9],grid[9],cnt[1<<9+5],num[1<<9+5],tot;
int g(int x,int y){//获取九宫格的编号 
    return ((x/3)*3)+(y/3);
}
void flip(int i,int j,int k){//翻转二进制位,行列编号以及要用来翻转的数 
    row[i]^= (1<<k);
    col[j]^=(1<<k);
    grid[g(i,j)]^=(1<<k);
}
bool dfs(int now){//参数表示的是当前还剩余没填的位置数量 
    if(now==0)return true;
    
    int tmp=10,x,y; //扫描所有可以填数的位置,计算出可能性最大的位置 
    for(int i=0;i<9;i++)
        for(int j=0;j<9;j++){
            if(str[i][j]!='.')continue;
            int val=row[i]&col[j]&grid[g(i,j)];//能填的数的二进制位 
            if(cnt[val] < tmp){
                tmp=cnt[val];
                x=i;
                y=j;
            }
        }
        
    int val=row[x]&col[y]&grid[g(x,y)];
    while(val){//扫描所有的有效位 
        int can = num[val&(-val)];//取出最低有效位,计算这个二进制位映射的能填的数 
        flip(x,y,can); 
        str[x][y]='1'+can;
        if(dfs(now-1))return 1;
        flip(x,y,can);//回溯,还原状态 
        str[x][y]='.';
        val-=val&(-val);
    }
    return false;
}
int main(){
    for(int i=0;i<(1<<9);i++)
        for(int j=i;j;j-=(j&-j))cnt[i]++;//计算一种状态中有多少个可以填的数
    
    for(int i=0;i<9;i++)
        num[1<<i]=i;//通过二进制位映射到一个可以填的数
        
    char s[100];
    while(scanf("%s",s) && s[0]!='e'){
        tot=0;
        
        for(int i=0;i<9;i++)
            for(int j=0;j<9;j++)str[i][j]=s[i*9+j];//将一维数组变成二维数组
        
        for(int i=0;i<9;i++)row[i]=col[i]=grid[i]=(1<<9)-1;//计算行列和九宫格中没有用过的状态
        for(int i=0;i<9;i++)
            for(int j=0;j<9;j++){
                if(str[i][j]!='.')flip(i,j,str[i][j]-'1');
                else tot++;//搜索树的深度 
            }    
//        cout<<"tot:"<<tot<<endl;
        dfs(tot);
        for(int i=0;i<9;i++)
            for(int j=0;j<9;j++)s[i*9+j]=str[i][j];//二维数组转成一维输出
        
        puts(s); 
    }
}

 

posted @ 2020-06-19 10:27  WA自动机~  阅读(218)  评论(0编辑  收藏  举报