Sudoku(16*16)

Solution

一道神仙暴力剪枝题,思路是在9*9的数独之上,再多添加3个剪枝

1.判断每个空格中,如果一个字母都填不了就返回,如果只能填一个,就填上并继续搜索

2.对于每个字母,在每行\列\16宫格中判断能填的位置,如果没有就返回,如果只有一个就填上,并继续搜索

3.在上述剪枝完成后,再用位运算优化,取出最少的一个空格,并用lowbit运算取出能填的数

Attention!!!

1.宫格是真的恶心……

2.注意填过了和不能填的区别

详细见代码

#include<bits/stdc++.h>
using namespace std;
#define lb(a) (a&-a)
const int N=(1<<16);
char s[20][20];
int ok[20][20],cnt[N],num[N],kase,tot;//ok数组2的第i位为1表示可以填写 
bool print(){for(int i=1;i<=16;i++)printf("%s\n",s[i]+1);return false;}
void clear(){tot=0;for(int i=1;i<=16;i++)for(int j=1;j<=16;j++)ok[i][j]=N-1;}
void upd(int x,int y,int num){//更新的函数 
    for(int i=1;i<=16;i++){
        ok[x][i]&=~(1<<num);//
        ok[i][y]&=~(1<<num);//
    }
    for(int i=(x-1)/4*4+1;i<=(x-1)/4*4+4;i++)//宫格 
     for(int j=(y-1)/4*4+1;j<=(y-1)/4*4+4;j++)
      ok[i][j]&=~(1<<num);
}
bool dfs(int step){
    if(step==tot+1)
     return print();
    int ansi,ansj,mn=1e9;
    int ok2[20][20];
    memcpy(ok2,ok,sizeof(ok2));//因为此处操作比较复杂,所以我们先把数组复制到临时数组中,之后再还原 
    for(int i=1;i<=16;i++)
     for(int j=1;j<=16;j++){
        if(s[i][j]!='-')continue;//不是空格 
        if(!ok[i][j])return true;//不能填 
        if(cnt[ok[i][j]]==1){//只有一个可以填,就是剪枝1 
         s[i][j]=num[ok[i][j]]+'A',upd(i,j,num[ok[i][j]]);
         if(!dfs(step+1))return false;
         s[i][j]='-';
         memcpy(ok,ok2,sizeof(ok2));
         return true;
        } 
        if(cnt[ok[i][j]]<mn){//顺便找出空格最少的 
            mn=cnt[ok[i][j]];
            ansi=i,ansj=j;
        }
     }
    for(int i=0;i<16;i++){//剪枝2,行的剪枝 
       for(int j=1;j<=16;j++){
          int vis=0,nxt=0;
          bool fs=0;
          for(int k=1;k<=16;k++){
              if(s[j][k]=='A'+i)fs=1;//记得特判,填过了和不能填的差别 
              if((ok[j][k]>>i&1)&&s[j][k]=='-')
             ++vis,nxt=k;
          }
          if(fs)continue;//填过了就跳过 
          if(!vis)return true;//如果不可填就不行 
          if(vis==1){
           s[j][nxt]=i+'A';upd(j,nxt,i);
           if(!dfs(step+1))return false;
           memcpy(ok,ok2,sizeof(ok2));
           s[j][nxt]='-';
           return true;
          } 
       }
     } 
        for(int i=0;i<16;i++){//列的剪枝 
         for(int k=1;k<=16;k++){
            int vis=0,nxt=0;
            bool fs=0;
            for(int j=1;j<=16;j++){
             if(s[j][k]=='A'+i)fs=1;
             if((ok[j][k]>>i&1)&&s[j][k]=='-')
              ++vis,nxt=j;
            }
            if(fs==1)continue;
            if(!vis)return true;
            if(vis==1){
             s[nxt][k]=i+'A',upd(nxt,k,i);
             if(!dfs(step+1))return false;
             s[nxt][k]='-';
             memcpy(ok,ok2,sizeof(ok2));
             return true;
            } 
         }
        }
      for(int k=0;k<16;k++){//九宫格的剪枝 
       for(int x=1;x<=13;x+=4)
        for(int y=1;y<=13;y+=4){
          int nt=0,nxti=0,nxtj=0;
          bool fs=0;
          for(int i=x;i<x+4;++i)
           for(int j=y;j<y+4;++j){//zz错误 
               if(s[i][j]=='A'+k)fs=1;
               if((ok[i][j]>>k&1)&&s[i][j]=='-')
             ++nt,nxti=i,nxtj=j;
            if(nt>1)break;
           }
          if(fs)continue;
          if(!nt)return true;
          if(nt==1){
           s[nxti][nxtj]=k+'A',upd(nxti,nxtj,k);
           if(!dfs(step+1))return false;
           s[nxti][nxtj]='-';
          memcpy(ok,ok2,sizeof(ok2));
          return true;
        } 
     }
    }
    for(int tmp=ok[ansi][ansj];tmp;tmp-=lb(tmp)){//找最小的空格去填 
        s[ansi][ansj]=num[lb(tmp)]+'A';
        upd(ansi,ansj,num[lb(tmp)]);
        if(!dfs(step+1))return false;
        s[ansi][ansj]='-';//还原 
        memcpy(ok,ok2,sizeof(ok));
    } 
    return true;
}
void init(){//输入 
    clear();
    for(int i=1;i<=16;i++)
     scanf("%s", s[i]+1);//+1是指下标从一开始,比较方便 
    for(int i=1;i<=16;i++)
     for(int j=1;j<=16;j++)
      if(s[i][j]!='-')
       upd(i,j,s[i][j]-'A');//如果不是空格就更新 
      else ++tot;//否则计数 
    dfs(1);
}
int main(){
    int test;
    cin>>test;
    for(int i=0;i<16;i++)num[1<<i]=i;//预处理2的次数幂与对数的关系 
    for(int i=0;i<(1<<16);i++)
     for(int j=i;j;j-=lb(j))
      cnt[i]++;//预处理每一个数的1的个数 
     while(test--){
        if(kase++)puts("");
        init();
    }
} 

 

posted @ 2019-09-15 21:00  Coder_cjh  阅读(474)  评论(0编辑  收藏  举报