poj3076 Sudoku(DFS+剪枝)

题意

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

 

题解

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

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

 

代码

#include
#include
#include
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<>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<0) continue;
        if((table[x][y]&1<0) continue;
            if((table[r+i][c+j]&1<>=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

 

你可能感兴趣的:(刷题之路,递归/DFS,《算法竞赛进阶指南》刷书之旅)