zoukankan      html  css  js  c++  java
  • 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
    ",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();
        }
    } 
  • 相关阅读:
    Triangle LOVE
    数据传送指令具体解释
    关于C++String字符串的使用
    TCP/IP基础(一)
    java打开目录(含推断操作系统工具类和解压缩工具类)
    hdu-1848 Fibonacci again and again
    opencv2对读书笔记——图像二值化——thresholded函数
    安卓中四种点击事件
    @MappedSuperclass注解的使用说明
    Androidclient採用Http 协议Post方式请求与服务端进行数据交互
  • 原文地址:https://www.cnblogs.com/coder-cjh/p/11524218.html
Copyright © 2011-2022 走看看