码迷,mamicode.com
首页 > 其他好文 > 详细

Sudoku(16*16)

时间:2019-09-15 21:14:51      阅读:130      评论:0      收藏:0      [点我收藏+]

标签:include   ons   att   style   详细   turn   attention   复制   的区别   

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();
    }
} 

 

Sudoku(16*16)

标签:include   ons   att   style   详细   turn   attention   复制   的区别   

原文地址:https://www.cnblogs.com/coder-cjh/p/11524218.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!