zoukankan      html  css  js  c++  java
  • 数独

    求解数独问题的基本方法是在(dfs)的过程中剪枝,下面这几种方法的剪枝强度递增
    朴素方法
    通过数组记录每行、每列、每个九宫格中已经填过的数字,从左上角的空格开始(dfs),填入所在行、列和九宫格中都没有出现过的数字
    相关题目:hdu1426 Sudoku Killer

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cstring>
    #include<string>
    #include<sstream>
    #include<cmath>
    #include<ctime>
    #include<algorithm>
    #define LL long long
    #define PII pair<int,int>
    #define PLL pair<LL,LL>
    #define pi acos(-1.0)
    #define eps 1e-6
    #define lowbit(x) x&(-x)
    using namespace std;
    
    const int N=9;
    int R[N+5][N+5],C[N+5][N+5],S[N][N][N+5];
    int b[N+5][N+5];
    char a[N];
    
    bool dfs(int x,int y){
        if(y==N){
            y=0;
            x++;
        }
        if(x==N) return true;
        if(b[x][y]){
            if(dfs(x,y+1)) return true;
        }
        else{
            for(int num=1;num<=N;num++){
                if(!R[x][num] && !C[y][num] && !S[x/3][y/3][num]){
                    R[x][num]=1;
                    C[y][num]=1;
                    S[x/3][y/3][num]=1;
                    b[x][y]=num;
                    if(dfs(x,y+1)) return true;
                    b[x][y]=0;
                    R[x][num]=0;
                    C[y][num]=0;
                    S[x/3][y/3][num]=0;
                }
            }
        }
        return false;
    }
    
    int main(void){
        bool first=true;
        while(scanf("%s",a)!=EOF){
            memset(R,0,sizeof(R));
            memset(C,0,sizeof(C));
            memset(S,0,sizeof(S));
            if(a[0]=='?') b[0][0]=0;
            else{
                b[0][0]=a[0]-'0';
                R[0][b[0][0]]=1;
                C[0][b[0][0]]=1;
                S[0][0][b[0][0]]=1;
            }
            for(int i=0;i<N;i++){
                for(int j=0;j<N;j++){
                    if(i==0 && j==0) continue;
                    scanf("%s",a);
                    if(a[0]=='?'){
                        b[i][j]=0;
                    }
                    else{
                        b[i][j]=a[0]-'0';
                        R[i][b[i][j]]=1;
                        C[j][b[i][j]]=1;
                        S[i/3][j/3][b[i][j]]=1;
                    }
                }
            }
            if(dfs(0,0)){
                if(first) first=false;
                else printf("
    ");
                for(int i=0;i<N;i++){
                    printf("%d",b[i][0]);
                    for(int j=1;j<N;j++){
                        printf(" %d",b[i][j]);
                    }
                    printf("
    ");
                }
            }
        }
        return 0;
    }
    

    二进制压缩优化
    1.通过二进制压缩记录出现过的数字,数位上为(1)表示这个数字还没有使用过,通过(lowbit)操作得到二进制表示的状态下所有可填的数字
    2.每次(dfs)时遍历所有格子,选择候选数字数目最小的空格(二进制中(1)的个数最少)填数字
    相关题目:hdu3111 Sudoku

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cstring>
    #include<string>
    #include<sstream>
    #include<cmath>
    #include<ctime>
    #include<algorithm>
    #define LL long long
    #define PII pair<int,int>
    #define PLL pair<LL,LL>
    #define pi acos(-1.0)
    #define eps 1e-6
    #define lowbit(x) x&(-x)
    using namespace std;
    
    const int N=9,maxx=1<<N;
    int T,R[15],C[15],S[5][5];
    int ones[maxx+10],number[maxx+10];
    char a[N+5][N+5],b[N];
    
    void init(){
        for(int i=0;i<maxx;i++){
            int s=0;
            for(int j=i;j;j-=lowbit(j)){
                s++;
            }
            ones[i]=s;
        }
        for(int i=0;i<N;i++) number[1<<i]=i;
    }
    
    void init2(){
        for(int i=0;i<N;i++){
            R[i]=C[i]=maxx-1;
            for(int j=0;j<N;j++){
                S[i/3][j/3]=maxx-1;
            }
        }
    }
    
    int sum(int x,int y){
        return R[x]&C[y]&S[x/3][y/3];
    }
    
    bool dfs(int rest){
        if(rest==0) return true;
        int x,y,mini=10;
        for(int i=0;i<N;i++){
            for(int j=0;j<N;j++){
                if(a[i][j]=='?'){
                    int t=ones[sum(i,j)];
                    if(mini>t){
                        x=i;
                        y=j;
                        mini=t;
                    }
                }
            }
        }
        int X=sum(x,y);
        for(int i=X;i;i-=lowbit(i)){
            R[x]-=lowbit(i);
            C[y]-=lowbit(i);
            S[x/3][y/3]-=lowbit(i);
            a[x][y]='1'+number[lowbit(i)];
            if(dfs(rest-1)) return true;
            a[x][y]='?';
            R[x]+=lowbit(i);
            C[y]+=lowbit(i);
            S[x/3][y/3]+=lowbit(i);
        }
        return false;
    }
    
    int main(void){
        init();
        scanf("%d",&T);
        for(int cas=1;cas<=T;cas++){
            if(cas!=1) scanf("%s",b);
            int cnt=0;
            init2();
            for(int i=0;i<N;i++){
                scanf("%s",a[i]);
                for(int j=0;j<N;j++){
                    if(a[i][j]=='?'){
                        cnt++;
                    }
                    else{
                        int bit=a[i][j]-'1';
                        R[i]-=1<<bit;
                        C[j]-=1<<bit;
                        S[i/3][j/3]-=1<<bit;
                    }
                }
            }
            if(cas!=1) printf("---
    ");
            if(dfs(cnt)){
                for(int i=0;i<N;i++){
                    printf("%s
    ",a[i]);
                }
            }
            else{
                printf("impossible
    ");
            }
        }
        return 0;
    }
    

    从分支最少的部分开始搜索(16*16数独)
    1.如果一个位置只能填一个数字,那么就填上这个数字
    2.枚举每一个数字,判断在每行、每列、每个十六宫格中有没有可以填的地方。如果只有一个地方可以填,就填上这个数字,如果没有位置可以填,直接回溯
    3.选取可能填的数字最少的格子,枚举所有情况进行(dfs)

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<cstring>
    #include<string>
    #include<sstream>
    #include<cmath>
    #include<ctime>
    #include<algorithm>
    #define LL long long
    #define PII pair<int,int>
    #define PLL pair<LL,LL>
    #define pi acos(-1.0)
    #define eps 1e-6
    #define lowbit(x) x&(-x)
    using namespace std;
    
    const int N=16;
    int num,a[N+5][N+5],table[N+5][N+5];
    char s[N+5];
    //在某个位置填入数字,同时更新填入这个数字之后的影响
    void put_in(int x,int y,int k){
        num++;
        a[x][y]=k;
        table[x][y]|=1<<k;
        for(int i=0;i<N;i++){
            table[x][i]|=1<<k;
            table[i][y]|=1<<k;
        }
        int r=x/4*4;
        int c=y/4*4;
        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                table[r+i][c+j]|=1<<k;
            }
        }
    }
    
    int _count(int x){
        for(int i=0;x;i++){
            if(x&1){
                if(x>>1==0) return i;
                return -1;
            }
            x>>=1;
        }
        return -1;
    }
    
    int row(int x,int k){
        int t=-1;
        for(int y=0;y<N;y++){
            if(a[x][y]==k) return -1;
            if(a[x][y]>=0) continue;
            if((table[x][y]&1<<k)==0){
                if(t!=-1) return -1;
                t=y;
            }
        }
        if(t!=-1) return t;
        return -2;
    }
    
    int col(int y,int k){
        int t=-1;
        for(int x=0;x<N;x++){
            if(a[x][y]==k) return -1;
            if(a[x][y]>=0) continue;
            if((table[x][y]&1<<k)==0){
                if(t!=-1) return -1;
                t=x;
            }
        }
        if(t!=-1) return t;
        return -2;
    }
    
    void cube(int r,int c,int k,int& x,int& y){
        x=-2;
        for(int i=0;i<4;i++){
            for(int j=0;j<4;j++){
                if(a[r+i][c+j]==k){
                    x=-1;
                    return;
                }
                if(a[r+i][c+j]>=0) continue;
                if((table[r+i][c+j]&1<<k)==0){
                    if(x!=-2){
                        x=-1;
                        return;
                    }
                    x=i;
                    y=j;
                }
            }
        }
    }
    //计算一个位置中已经填入的数字个数
    int cal(int x){
        int cnt=0;
        while(x){
            if(x&1) cnt++;
            x>>=1;
        }
        return cnt;
    }
    
    bool dfs(){
        if(num==256) return true;
        //判断是否存在一个位置,只能填一个数字
        for(int i=0;i<N;i++){
            for(int j=0;j<N;j++){
                if(a[i][j]>=0) continue;
                int k=_count(table[i][j]);
                if(k!=-1) put_in(i,j,k);
            }
        }
        //判断某行是否只能填一个数字
        for(int i=0;i<N;i++){
            for(int k=0;k<N;k++){
                int j=row(i,k);
                if(j==-2) return false;
                if(j!=-1) put_in(i,j,k);
            }
        }
        //判断某列是否只能填一个数字
        for(int j=0;j<N;j++){
            for(int k=0;k<N;k++){
                int i=col(j,k);
                if(i==-2) return false;
                if(i!=-1) put_in(i,j,k);
            }
        }
        //判断某个十六宫格是否只能填一个数字
        for(int i=0;i<N;i+=4){
            for(int j=0;j<N;j+=4){
                for(int k=0;k<N;k++){
                    int x,y;
                    cube(i,j,k,x,y);
                    if(x==-2) return false;
                    if(x!=-1) put_in(i+x,j+y,k);
                }
            }
        }
        if(num==256) return true;
        //寻找可以填的数字最少的格子开始搜索
        int t_num,t_table[N+5][N+5],t_a[N+5][N+5];
        t_num=num;
        for(int i=0;i<N;i++){
            for(int j=0;j<N;j++){
                t_table[i][j]=table[i][j];
                t_a[i][j]=a[i][j];
            }
        }
        int mx,my,mn=16;
        for(int i=0;i<N;i++){
            for(int j=0;j<N;j++){
                if(a[i][j]>=0) continue;
                int r=16-cal(table[i][j]);
                if(r<mn){
                    mn=r;
                    mx=i;
                    my=j;
                }
            }
        }
        for(int k=0;k<N;k++){
            if((table[mx][my]&1<<k)==0){
                put_in(mx,my,k);
                if(dfs()) return true;
                num=t_num;
                for(int i=0;i<N;i++){
                    for(int j=0;j<N;j++){
                        a[i][j]=t_a[i][j];
                        table[i][j]=t_table[i][j];
                    }
                }
            }
        }
        return false;
    }
    
    int main(void){
        while(scanf("%s",s)!=EOF){
            num=0;
            memset(table,0,sizeof(table));
            for(int j=0;j<N;j++){
                if(s[j]!='-'){
                    put_in(0,j,s[j]-'A');
                }
                else a[0][j]=-1;
            }
            for(int i=1;i<N;i++){
                scanf("%s",s);
                for(int j=0;j<N;j++){
                    if(s[j]!='-'){
                        put_in(i,j,s[j]-'A');
                    }
                    else a[i][j]=-1;
                }
            }
            if(dfs()){
                for(int i=0;i<N;i++){
                    for(int j=0;j<N;j++){
                        printf("%c",a[i][j]+'A');
                    }
                    printf("
    ");
                }
            }
        }
        return 0;
    }
    
  • 相关阅读:
    Java 编译器
    ElasticSearch 集群搭建
    致:奋斗路上的自己
    ElasticSearch 简单入门
    char* 和 char* const
    usb虚拟网卡与串口
    usb虚拟网卡与串口
    ethtool处理网卡不断重启
    客车网上订票系统项目--票务管理、前端个人信息修改
    mysql错误号码2003 can't connect to mysql server on 'localhost' (0)解决方案
  • 原文地址:https://www.cnblogs.com/fxq1304/p/13434368.html
Copyright © 2011-2022 走看看