递归回溯求解数独 C++实现方法

用递归回溯法求解9*9数独的C++实现,附详细说明
本人初学,有不当之处望大神指正


代码块

//风休住于2018.8.31编写完成
//All Rights Reserved 
#include<bits/stdc++.h>
using namespace std;

/*数独二维数组*/
int board[9][9];

/*声明Check函数,用以检查所填的数是否合法*/
bool Check(int check_number,int check_now_line,int check_now_column,
    int check_block_line,int check_block_column);

/*输入函数*/
void Input();

/*工作函数*/
bool Work(int now_line,int now_column);

/*输出函数*/
void Output();

/*主函数*/
int main()
{
 Input();
 if(Work(0,0)) Output();//如果有解,则调用输出函数 
 else cout<<"No Answer."<<endl;//如果无解,则输出“No Answer.” 
 system("pause"); 
 return 0;
}

/*check_number为待检查的数字,check_now_line和check_now_column为待检查的坐标,
check_block_line和check_block_column为所在的3*3方格左上角坐标*/
bool Check(int check_number,int check_now_line,int check_now_column,
    int check_block_line,int check_block_column)
{
    for(int i=0;i<=8;++i)//检查与待检查的坐标同行或同列的位置 
    {
        if(board[check_now_line][i]==check_number||board[i][check_now_column]==check_number) 
            return false;
    }
    for(int i=0;i<=2;++i)//检查与待检查的坐标同3*3方格的位置 
    {
        for(int j=0;j<=2;++j)
        {
            if(board[check_block_line+i][check_block_column+j]==check_number)
                return false;
        }
    }
    return true;//通过所有检查,返回true 
}

void Input()
{
    cout<<"请输入一个9*9数独,两数见用空格隔开,空位用0表示:"<<endl; 
    for(int i=0;i<=8;++i)
    {
        for(int j=0;j<=8;++j)
        {
            cin>>board[i][j];
        }
    }
}

/*为使代码更为简洁,将Work函数定义为bool,如果数独有解则返回true,无解则返回false,
不用再单独定义flag来标记数独是否完成*/ 
bool Work(int now_line,int now_column)
{
    if(now_line==9)
    {
        return true;//如果将数独解完,返回true 
    }
    else 
    {
        int next_line,next_column,block_line,block_column;
        next_column=now_column+1;
        next_line=(next_column>=9?now_line+1:now_line);
        next_column=(next_column>=9?0:next_column);
        if(board[now_line][now_column]!=0)//如果当前坐标有数字,则对下一个坐标进行工作 
        {
            if(Work(next_line,next_column)) return true;//如果数独最终有解,则不断向前返回true 
        }
        else
        {
            block_line=(now_line/3)*3;//计算所在的3*3方格左上角坐标
            block_column=(now_column/3)*3;
            for(int i=1;i<=9;++i)
            {
                if(Check(i,now_line,now_column,block_line,block_column))
                {
                    board[now_line][now_column]=i;//如果i值合法,则对下一个坐标进行工作
                    if(Work(next_line,next_column)) return true;
                }
            }
            board[now_line][now_column]=0;//回溯操作 
            return false;//如果i的值为1-9均不合法,则返回上一层继续循环 
        }
    }
}

void Output()
{
    cout<<endl<<endl;
    for(int i=0;i<=8;++i)
    {
        for(int j=0;j<=8;++j)
        {
            cout<<setw(2)<<board[i][j];
            if((j+1)%3==0) cout<<" ";
        }
        cout<<endl;
        if((i+1)%3==0) cout<<endl;
    }
}

说明

数独规则
数独是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复。—— [ 百度百科 ]

数独模型

首先对Check函数进行一下说明。假设正在对(2,4)号方块进行检查,首先需保证与(2,4)同一行及同一列上的方格中,没有与待填数字相同的方格;其次,需保证在图中标绿色的3*3方格中,没有与待填数字相同的方格,代码实现为

for(int i=0;i<=8;++i)
{
    if(board[check_now_line][i]==check_number||board[i][check_now_column]==check_number) 
        return false;
}
for(int i=0;i<=2;++i)
{
    for(int j=0;j<=2;++j)
    {
        if(board[check_block_line+i][check_block_column+j]==check_number)
            return false;
    }
}

如果通过了以上检验,则
return true

在进行求解时,主函数调用 Work(0,0) 即从方格(0,0)处开始工作,从左到右、从上到下依次进行。
如果该方格内已经填过数,则跳过这一方格,形如
if(board[now_line][now_column]!=0) { if(Work(next_line,next_column)) return true; }
接下来开始对当前方格进行处理。为了方便 Check 函数的运行,首先将计算出当前方格所处 Block 的左上角坐标,形如
block_line=(now_line/3)*3; block_column=(now_column/3)*3;
其次,检查1-9填入当前方块中是否合法,如果合法,则不管它是否正确,直接填入当前方块中,并对下一个方块进行工作,形如
if(Check(i,now_line,now_column,block_line,block_column)){board[now_line][now_column]=i;if(Work(next_line,next_column)) return true; }
像这样运行几次后,有可能出现这种情况,即先前填写的数字有错误,导致当前方块一个数也填不了;那么就会跳出检查1-9的for循环,执行以下操作
board[now_line][now_column]=0; return false;
第一句目的是将当前方块赋0(递归回溯基本操作),第二句使函数返回false,则跳出当前层次的递归,回到上一个检查1-9的for循环;如果依然没有可填的数,则再回到上一个循环,以此类推。如果第一个方格也无数可填,则主函数中的返回值为false,执行
cout<<"No Answer."<<endl;
当工作到方块(8,8)时,如果仍然找到了可填的数,则将开始对第10行进行工作。这是将会触发
if(now_line==9) {return true;}
这也意味着数独成功找到了一种解法,第81层递归返回true。在第80层递归收到true时,它也会返回true,以此类推,主函数中的返回值为true,执行
Output();
将数独数组输出,结束

特点及缺陷

关键代码 if(Work(next_line,next_column)) return true;
这段代码利用了C++语言简洁的特征,在对数独是否有解进行判断(上文中有具体说明)的同时,运行了Work函数,填写了整个数独。
缺陷在于,这篇代码纯粹利用了计算机运行速度快的特点,对所有空格进行了暴力枚举,并没有使用人工解数独时的技巧方法,有些遗憾。

测试数据

输入

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

输出

2 9 6 1 7 5 4 3 8
4 5 1 8 3 2 7 9 6
3 8 7 9 6 4 2 5 1
6 1 5 4 9 8 3 2 7
8 2 9 7 1 3 6 4 5
7 3 4 5 2 6 1 8 9
5 7 2 3 8 1 9 6 4
9 6 8 2 4 7 5 1 3
1 4 3 6 5 9 8 7 2

输入

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

输出

9 6 1 4 8 7 5 3 2
7 8 3 2 5 1 9 6 4
5 2 4 6 3 9 7 1 8

4 9 8 3 1 6 2 5 7
2 1 6 5 7 4 3 8 9
3 7 5 8 9 2 6 4 1

6 5 7 1 2 8 4 9 3
8 4 9 7 6 3 1 2 5
1 3 2 9 4 5 8 7 6

  • 8
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值