Sudoku(16*16)

Posted coder-cjh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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();
    
 

 

以上是关于Sudoku(16*16)的主要内容,如果未能解决你的问题,请参考以下文章

[DLX+bfs] hdu 4069 Squiggly Sudoku

Sudoku Solver & Valid Sudoku

36. Valid Sudoku/37. Sudoku Solver - 数独问题-- backtracking 经典

37. Sudoku Solver

Valid Sudoku

poj 2676 Sudoku