poj3076 Sudoku(DFS+剪枝)

版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/81950224

题意

用A~P填写一个16*16的数独。

题解

DFS+超强剪枝
1、搜索每一个位置可以填的数,如果只有一个,立刻填上;如果没有可以填的数,立刻回溯。
2、枚举一个数字,在每个行\列\宫格中,有没有可以填的地方。如果只有一个,将其填上;如果无法填上,立刻回溯。
3、选取一个可能情况最少的格子,枚举其所有情况。
4、dfs(k+1),重复执行以上操作。

以上1和2的操作是有区别的:1只有当一个格子所处的行\列\宫格已经把除X外的数字填过时,才会将其填上,它们是直接影响到这个格子的;2是说除了这个格子外,(同一行\列\宫格中)其它的格子由于某种原因,都不能填X,只有这个格子可以填时,就可以填了。
对于每个位置可填的数,我们用一个二进制数来记录,这样能优化常数。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=20;

int map[maxn][maxn];
unsigned short table[maxn][maxn];//talbe[i][j]表示(i,j)可以填的数有哪些,0可填,1不可填 
int filled;

void fill(int x,int y,int a)//(x,y)填a
{
    filled++;
    map[x][y]=a;
    table[x][y]|=1<<a-1;//
    for(int i=0;i<16;i++) table[x][i]|=1<<a-1,table[i][y]|=1<<a-1;
    int r=x/4*4,c=y/4*4;
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++) table[r+i][c+j]|=1<<a-1;
}

int count_one(unsigned short x)//判断x中1的个数是否只有1个 
{
    for(int i=0;x;i++)
    {
        if(x&1)
        {
            if(x>>1==0) return i;
            return -1;
        }
        x>>=1;
    }
    return -1;
}

int hang(int x,int k)//第x行,数字k+1。返回>0表示唯一可填k+1的位置;返回-1表示出现超过1次;返回-2表示k+1没有可以填的位置 
{
    int p=-1;
    for(int y=0;y<16;y++)
    {
        if(map[x][y]==k+1) return -1;//数字k+1已填
        if(map[x][y]>0) continue;
        if((table[x][y]&1<<k)==0)
        {
            if(p!=-1) return -1;//出现超过1次 
            p=y;
        }
    }
    if(p!=-1) return p; 
    return -2;
}

int lie(int y,int k)//第y行,数字k 
{
    int p=-1;
    for(int x=0;x<16;x++)
    {
        if(map[x][y]==k+1) return -1;
        if(map[x][y]>0) continue;
        if((table[x][y]&1<<k)==0)
        {
            if(p!=-1) return -1;
            p=x;
        }
    }
    if(p!=-1) return p;
    return -2;
}

void gong(int r,int c,int k,int &x,int &y)//以(r,c)为左上角的宫格,数字k+1,(x,y)为唯一可填坐标 
{
    x=-2;
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++)
        {
            if(map[r+i][c+j]==k+1){x=-1;return ;}
            if(map[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 count_1(unsigned short x)//求x中1的个数 
{
    int re=0;
    while(x)
    {
        if(x&1) re++;
        x>>=1;
    }
    return re;
}

bool search()
{
    if(filled==256) return true;
    //只有一个数字可以填的格子先处理 
    for(int x=0;x<16;x++)
        for(int y=0;y<16;y++)
        {
            if(map[x][y]>0) continue; 
            int k=count_one(table[x][y]);
            if(k!=-1) fill(x,y,k+1);
        }
    //数字k+1在行\列\宫格中可填情况 
    for(int x=0;x<16;x++)
        for(int k=0;k<16;k++)
        {
            int y=hang(x,k);
            if(y==-2) return false;//回溯 
            if(y!=-1) fill(x,y,k+1);
        }
    for(int y=0;y<16;y++)
        for(int k=0;k<16;k++)
        {
            int x=lie(y,k);
            if(x==-2) return false;//回溯 
            if(x!=-1) fill(x,y,k+1);
        }
    for(int r=0;r<16;r+=4)
        for(int c=0;c<16;c+=4)
            for(int k=0;k<16;k++)
            {
                int x,y;
                gong(r,c,k,x,y);
                if(x==-2) return false;//回溯 
                if(x!=-1) fill(r+x,c+y,k+1);
            }
    
    if(filled==256) return true;
    //备份 
    int t_filled;
    int t_map[maxn][maxn];
    unsigned short t_table[maxn][maxn];
    t_filled=filled;
    for(int i=0;i<16;i++)
        for(int j=0;j<16;j++)
        {
            t_map[i][j]=map[i][j];
            t_table[i][j]=table[i][j];
        }
    //找可能情况最少的格子来枚举 
    int mx,my,mn=16;
    for(int i=0;i<16;i++)
        for(int j=0;j<16;j++)
        {
            if(map[i][j]>0) continue;
            int r=16-count_1(table[i][j]);
            if(r<mn)
            {
                mn=r;
                mx=i;my=j;
            }
        }
    for(int k=0;k<16;k++)
    {
        if((table[mx][my]&1<<k)==0)
        {
            fill(mx,my,k+1);
            if(search()) return true;
            filled=t_filled;
            for(int i=0;i<16;i++)
                for(int j=0;j<16;j++)
                {
                    map[i][j]=t_map[i][j];
                    table[i][j]=t_table[i][j];
                }
        }
    }
    
    return false;
}

char ar[maxn];
int main()
{
    while(1)
    {
        filled=0;
        memset(map,0,sizeof(map));
        memset(table,0,sizeof(table));
        for(int i=0;i<16;i++)
        {
            if(scanf("%s",ar)==EOF) return 0;
            for(int j=0;j<16;j++)
            {
                if(ar[j]!='-') fill(i,j,ar[j]-'A'+1);
            }
        }
        
        search();
        
        for(int i=0;i<16;i++)
        {
            for(int j=0;j<16;j++) printf("%c",map[i][j]+'A'-1);
            printf("\n");
        }
        printf("\n");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/81950224