求解数独

数独是一种折磨人的游戏,感兴趣却不懂者随便baidu一下就知道其规则了

我的这个解法原理是先根据数独的规则,找出能快速确定的数字:
    1,这一点能且仅能取某值;
    2,这一行 / 列 / 格 没有其他的点能取某值。

然后如果没有解出该数独则再用回溯法搜索解空间。

此算法的效率相当不错。


class sudoku
{
    char board_map [9][9];//盘面
    char background[9][9];//作为记录scan结果用的盘面
    bool possible  [9][9][10];     //某个格子里某个元素是否可能出现
    char remaind   [9][9];        //某个格子中还有几种可能的数字
    void init()
    {
        for(int i=0;i<9;i++)
        {   
            for(int j=0;j<9;j++)
            {
                remaind[i][j]=9;
                for(int k=0;k<=9;k++)possible[i][j][k]=true;
                background[i][j]=0;
            }
        }
    }
 public:
    sudoku(char b[9][9])
    {
        init();
        for(int i=0;i<9;i++)
            for(int j=0;j<9;j++)
                {
                    board_map[i][j]=b[i][j];
                    background[i][j]=0;
                }
    }
    sudoku()
    {
        init();
        for(int i=0;i<9;i++)
            for(int j=0;j<9;j++)
                background[i][j]=board_map[i][j]=0;
    }
    ~sudoku(){}
   
    void get_result(char b[9][9])
    {for(int x=0;x<9;x++)for(int y=0;y<9;y++)b[x][y]= board_map[x][y];}
 private:
    void scan(int x,int y,int n);
    bool trace_back();
 public:
    bool search();
};


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////


void sudoku::scan(int x,int y,int n)
{
    //cout<<"scaning ---> "<<x+1<<','<<y+1<<':'<<n<<'/n';
    if(board_map[x][y]==0) return;
    int i,j,k;
    int box_x=int(x/3) , box_y=int(y/3);
   
//---------------------------------------------------------------------------------
   
    for(i=0;i<9;i++)
    {
        if(possible[i][y][n])
        {
            possible[i][y][n] = false;
            if(remaind[i][y]!=1) remaind[i][y]--;
            if(remaind[i][y]==1 && board_map[i][y]==0)
            {
                for(k=1;k<=9;k++)if(possible[i][y][k]){background[i][y]=k;break;}
            }
        }
    }
    for(j=0;j<9;j++)
    {
        if(possible[x][j][n])
        {
            possible[x][j][n] = false;
            if(remaind[x][j]!=1) remaind[x][j]--;
            if(remaind[x][j]==1 && board_map[x][j]==0)
            {
                for(k=1;k<=9;k++)if(possible[x][j][k]){background[x][j]=k;break;}
            }
        }
    }
    for(i=box_x*3;i<box_x*3+3;i++)
        for(j=box_y*3;j<box_y*3+3;j++)
        {
            if(possible[i][j][n])
            {
                possible[i][j][n] = false;
                if(remaind[i][j]!=1) remaind[i][j]--;
                if(remaind[i][j]==1 && board_map[i][j]==0)
                {
                    for(k=1;k<=9;k++)if(possible[i][j][k]){background[i][j]=k;break;}
                }
            }
        }
    for(k=0;k<=9;k++) possible[x][y][k] = false;
    possible[x][y][n]=true;
    remaind [x][y]=0;
   
//---------------------------------------------------------------------------------
   
    int p=0;
    int ti,tj,tk;
    for(k=1;k<=9;k++)
    {
        p=0;
        for(i=0;i<9;i++)
        {
            if(possible[i][y][k])
            {
                p++;
                ti=i;
                tk=k;
            }
        }
        if(p==1) background[ti][y]=tk;
    }
   
    for(k=1;k<=9;k++)
    {
        p=0;
        for(j=0;j<9;j++)
        {
            if(possible[x][j][k])
            {
                p++;
                tj=j;
                tk=k;
            }
        }
        if(p==1) background[x][tj]=tk;   
    }
   
    for(k=1;k<=9;k++)
    {
        p=0;
        for(i=box_x*3;i<box_x*3+3;i++)
            for(j=box_y*3;j<box_y*3+3;j++)
            {
                if(possible[i][j][k])
                {
                    p++;
                    ti=i;
                    tj=j;
                    tk=k;
                }
            }
        if(p==1) background[ti][tj]=tk;                   
    }
}

bool sudoku::trace_back()//用回溯法进行搜索!
{
    int x=0,y=0,k,n;
    bool find_one=false;

    bool in_column[9][10];//true=>能放入第n行
    bool in_row[9][10];      //true=>能放入第n列
    bool in_box[3][3][10];//true=>能放入第n个格子
    for(n=1;n<=9;n++)//初始化三个可恶的数组
        for(x=0;x<9;x++)
        {
            in_column[x][n]=true;
            in_row[x][n]=true;
            in_box[x/3][x%3][n]=true;
        }
   
    for(n=1;n<=9;n++)
    for(x=0;x<9;x++)
        for(y=0;y<9;y++)
        {
            if(board_map[x][y]==n)
            {
                in_column[x][n]=false;
                in_box[x/3][y/3][n]=false;
                in_row[y][n]=false;
            }
        }
       
    x=0,y=0;     

    while(true)
    {
        if (x<0||y<0)//找到了所有结果
            break;
        if (x>8||y>8)//找到了一个结果 ,这里可以改一下,让他将找到的结果输出,然后继续寻找下一个结果
            break;
       
        while(board_map[x][y]!=0)//到下一个非预设点
        {
            if(++x==9)
            {
                x=0;
                ++y;
            }
            if(y>8) break;
         }    
        
         find_one=false;
        for(n=background[x][y]+1;n<=9;n++)
            if (
                in_column[x][n]&&
                in_row[y][n]&&
                in_box[x/3][y/3][n]
                )//可以放入
            {
                if((k=background[x][y])!=0)
                {
                    in_column[x][k]=true;
                    in_row[y][k]=true;
                    in_box[x/3][y/3][k]=true;
                }
               
                background[x][y]=n;
               
                in_column[x][n]=false;
                in_row[y][n]=false;
                in_box[x/3][y/3][n]=false;
               
                find_one=true;
                break;
            }
       
        if(find_one)
        {
            do    //到下一个非预设点
            {
                if(++x==9)
                {
                    x=0;
                    ++y;
                }
                if(y>8) break;
             } while(board_map[x][y]!=0);
        }
        else
        {//惨了走不动了,那就回去吧!
           
            in_column[x][background[x][y]]=true;
            in_row[y][background[x][y]]=true;
            in_box[x/3][y/3][background[x][y]]=true;
           
            background[x][y]=0;
            do
            {
                if(--x<0)
                {
                    x=8;
                    --y;
                }
                if(y<0)break;
            }while(board_map[x][y]!=0);
        }
    }
   
    int sum=0;
    for(x=0;x<9;x++)for(y=0;y<9;y++)
    {
        if(background[x][y]!=0)board_map[x][y]=background[x][y];
        sum+=board_map[x][y];
    }
    if (sum==45*9) return true;
    else return false;
}


bool sudoku::search()
{
    bool find_ans=false,new_ans_finded;
    int x,y;
    for(x=0;x<9;x++)
        for(y=0;y<9;y++)
            scan(x,y,board_map[x][y]);
    do
    {
        new_ans_finded=false;
        for(x=0;x<9;x++)
            for(y=0;y<9;y++)
            {
                if (background[x][y]!=0&&board_map[x][y]==0)
                {
                    new_ans_finded=true;
                    board_map[x][y]=background[x][y];
                    scan(x,y,board_map[x][y]);
                    //cout<<"* finded:"<<x+1<<','<<y+1<<':'<<(int)background[x][y]<<'/n';
                }
            }
    }
    while (new_ans_finded);
    unsigned int sum=0;
    for(x=0;x<9;x++)for(y=0;y<9;y++)sum+=board_map[x][y];
    if (sum==45*9) return true;
    else return trace_back();//此路不通,换一条试试!
}


我把这篇文章发表到网易博客时被人批评说注释太少——是太少了,凑合着看吧


另外给出一个简单的主函数,大家可以用来验证一下它的正确性以及效率:


int main()
{

 char s[4][9][9]=
 {
  0,2,0, 0,0,0, 0,0,0,
  0,0,1, 5,0,0, 0,0,2,
  0,6,0, 7,0,0, 0,0,0,
  
  1,0,0, 3,0,0, 0,0,0,
  0,7,8, 0,0,2, 0,0,0,
  0,0,5, 0,9,0, 0,0,3,
  
  7,0,4, 0,0,0, 0,8,6,
  0,0,0, 0,3,0, 5,1,0,
  0,0,0, 4,1,0, 0,0,0
  
,       ////////////////////

  0,0,0, 0,4,0, 2,8,0,
  0,0,4, 0,0,1, 0,7,0,
  0,9,0, 0,0,8, 1,3,0,
  
  0,0,7, 0,6,0, 0,0,1,
  0,6,0, 0,7,4, 0,0,8,
  0,4,0, 0,0,5, 6,0,0,
  
  1,0,3, 0,0,0, 0,0,5,
  4,0,0, 0,1,0, 8,0,0,
  2,0,6, 0,0,7, 0,0,0
  
,       ////////////////////

  0,0,6, 0,0,0, 0,0,0,
  0,0,0, 0,0,0, 0,0,8,
  0,0,2, 4,0,1, 0,0,0,
  
  0,0,4, 0,0,0, 0,0,0,
  0,3,0, 0,8,0, 0,0,5,
  0,0,0, 0,0,9, 0,0,0,
  
  5,8,0, 0,7,0, 0,0,0,
  0,0,0, 0,0,0, 0,0,0,
  0,7,0, 0,0,3, 4,1,0
  
,       ////////////////////

  0,0,0, 6,0,0, 0,4,0,
  3,2,0, 0,0,0, 0,0,1,
  0,4,8, 0,0,1, 3,0,0,
  
  0,0,0, 5,0,0, 0,7,0,
  8,3,0, 0,1,0, 0,0,5,
  5,0,7, 0,0,6, 1,0,0,
  
  1,5,0, 0,0,8, 6,0,0,
  0,0,0, 0,0,0, 0,0,0,
  4,8,0, 0,0,9, 7,0,0
 };

 sudoku x;
 char t[9][9];
 for(int n=0;n<4;n++)
 {
  x.reset(s[n]);
  if(x.search()) cout<<"answer finded:/n";
  else cout<<"can't find answer/n";
  x.get_result(t);
  
  
  for(int i=0;i<9;i++)
  {
   if(i==0)cout<<"┏━┯━┯━┳━┯━┯━┳━┯━┯━┓/n┃";
   else if(i%3==0)cout<<"┣━┿━┿━╋━┿━┿━╋━┿━┿━┫/n┃";
   else cout<<"┠─┼─┼─╂─┼─┼─╂─┼─┼─┨/n┃";
   for(int j=0;j<9;j++)cout<<(s[n][i][j]?'*':' ')<<int(t[i][j])<<((j+1)%3!=0?"│":"┃");
   cout<<'/n';
  }
  cout<<"┗━┷━┷━┻━┷━┷━┻━┷━┷━┛/n";
 }
 cin.get();
 return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值