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

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

时间: 2024-11-09 10:02:01

Sudoku(16*16)的相关文章

写出C语言的地址对齐宏ALIGN(p,alignbytes),其中p是要对齐的地址,alignbytes是要对齐的字节数(2的N次方),比如说:ALIGN(13,16)=16

写出C语言的地址对齐宏ALIGN(p,alignbytes),其中p是要对齐的地址,alignbytes是要对齐的字节数(2的N次方),比如说:ALIGN(13,16)=16. 答案:#define ALIGN(p,alignbytes) ((void*)(((unsigned long)p+alignbytes-1)&~(alignbytes-1)))

不使用局部变量和for循环或其它循环打印出如m=19,n=2結果为2 4 8 16 16 8 4 2形式的串

public static void Main(string[] args) { int m = 19; int n = 2; m = Recursionmult(m, n); RecursionDivision(m, n); //测试 m = 35; n = 4; Console.WriteLine("\nm=" + m + ",n=" + n); m = Recursionmult(m, n); RecursionDivision(m, n); m = 40;

用51做的16*16点阵显示屏幕(ptotues仿真)

第一次写博客,来试试水.正好前几天搞一个单片机的仿真拿来分享 16*16的点阵显示屏,按下开始按键后,在显示屏上轮流显示"字符串1"字样.再次按下开始按键后,显示屏上无任何显示.按下切换后能显示"字符串2"字样等(可以设计很多切换字符串).且启动消隐的过程显示清晰无异样. /* ***************************************************** */ // 作 者:lk 系统时钟 : 11.0592MHZ // 版 本:V1.

java 16 -16 键盘输入数据求最大值

需求: 键盘录入多个数据,输入0时得到结果,输出最大的一个 分析: A:创建键盘录入 B:由于数据个数不确定,用集合接收 C:把键盘录入的数据放进集合中 D:因为没学集合的排序,所以得先把集合转成数组 public Object[] toArray() 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组. public <T> T[] toArray(T[] a) 按适当顺序(从第一个到最后一个元素)返回包含此列表中所有元素的数组:返回数组的运行时类型是指定数组的运行时类型.

16.16

1 import java.awt.Graphics; 2 import java.awt.event.ActionEvent; 3 import java.awt.event.ActionListener; 4 5 import javax.swing.*; 6 7 8 9 public class Test_16_16 extends JFrame{ 10 11 public Test_16_16(){ 12 add(new JP()); 13 } 14 public static void

2018.4.16 16周4次课

十六周四次课(4月16日) 19.12 添加自定义监控项目 19.13/19.14 配置邮件告警 19.15 测试告警 19.16 不发邮件的问题处理 19.12 添加自定义监控项目 前面提到过zabbix的优势,其中之一就是很方便地添加自定义监控项目,它虽然提供了丰富的模板,但依然不能满足各种各样的特殊需求.比如,想要监控Nginx的访问日志条数,此类个性化需求在zabbix的模板中是没有的.下面举一个实际的例子来教你添加自定义的监控项目,这涉及编写shell脚本.需求是:监控某台web服务器

2016年8月15日 星期一 --出埃及记 Exodus 16:16

This is what the LORD has commanded: `Each one is to gather as much as he needs. Take an omer for each person you have in your tent.'" 耶和华所吩咐的是这样,你们要按着各人的饭量,为帐棚里的人,按着人数收起来,各拿一俄梅珥.

C++开发人脸性别识别教程(16)——视频人脸性别识别

在之前的博文中我们已经能够顺利驱动摄像头来采集源图像,在这篇博文中将正式为其加入性别识别的代码,实现摄像头视频的人脸性别识别. 一.人脸检测 在得到摄像头采集的源图像之后,首先要做的就是对其进行人脸检测,将人脸区域分割出来.这步相对来说比较简单,只需在定时器时间触发函数中加入人脸检测的代码即可,这里给出OnTimer()函数的整体代码: void CGenderRecognitionMFCDlg::OnTimer(UINT_PTR nIDEvent) { /***********人脸检测并识别*

miniLCD12864 16引脚

显示图片 main.c #include<reg51.h>#include"st7565.h"//---存一个图片--//unsigned char code pic[]={/*-- 宽度x高度=128x64 --*/0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x07,0x57,0x53,0x57,0x07,0xFF,0x0F,0xE3,0x0F,0xEF,0xFF,