Sudoku POJ - 3074(数独+优化搜索顺序+位运算优化状态记录,检索和更新)

题意:传送门
题解:首先考虑一种策略,先填上“已经能够唯一确定的位置”,然后从那些填的比较满,选项比较少的位置实施突破。这样使得搜索树的规模大大降低,其次考虑的就是在搜索状态上的记录,检索和更新上的开销(影响程序运行的“常数”时间),可以使用位运算来代替数组执行"对各个位置所填数字的记录"以及"可填性的检查与统计",这样就代替了使得 27 次 − > 1 次 27次->1次 27>1
算法过程是:

  1. 对于每行每列,每个九宫格,分别用一个 9 9 9位二进制数保存哪些数字还可以填入。
  2. 对于每个位置,把它所在行、列、九宫格的 3 3 3个二进制数做 & \& &运算,得出该位置要填哪些数,用 l o w b i t lowbit lowbit运算就可以把能填的数取出。
  3. 优先搜索可填数字少的,当一个位置填上某些数之后,把该位置的行、列、九宫格记录的二进制数的对应位置改为 0 0 0,即可更新状态,回溯时改为 1 1 1恢复现场。

c o d e : code: code

#include
#include
#include
#include
using namespace std;
const int N=9;
int row[N],col[N],cell[5][5],ones[1<<N],map[1<<N];
char str[100];
inline 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;
        }
    }
}
inline 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,x,y;
    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(minv>t){
                    minv=t;x=i;y=j;
                }
            }
        }
    }
    for(int i=get(x,y);i;i-=lowbit(i)){
        int t=map[lowbit(i)];
        row[x]-=(1<<t);
        col[y]-=(1<<t);
        cell[x/3][y/3]-=(1<<t);
        str[x*9+y]='1'+t;
        if(dfs(cnt-1))return true;
        row[x]+=(1<<t);
        col[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++)ones[i]=ones[i>>1]+i%2;
    while(scanf("%s",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';
                    row[i]-=1<<t;
                    col[j]-=1<<t;
                    cell[i/3][j/3]-=1<<t;
                }else cnt++;
            }
        }
        dfs(cnt);
        cout<<str<<endl;
    }
    return 0;
}

你可能感兴趣的:(数独+剪枝)