C++解数独(深度搜索)

在这里插入图片描述
本题来自本人C语言实践课程的补充题目

/*
测试数据(世界上最难的数独)
800000000
003600000
070090200
050007000
000045700
000100030
001000068
008500010
090000400
*/
/*
思路:本体采用深度优先搜索,从棋盘的第一个位置开始搜索
首先调用pos函数存储每个空位上可能填的数
再从第一个空位开始,深度搜索所有的结果
当成功搜完最后一个元素时,回溯
这样就能完成解法
*/
#include<iostream>
using namespace std;
char sudoku[9][9];//定义数独棋盘,0表示此处为空,需要填数
char a[9][9];     //a棋盘为最终的正确答案棋盘
int ans[9][9][9]; //ans数组存放着9*9棋盘上每个位置上可以填的数
int res[9][9];    //res数组表示9*9棋盘上每个位置上可以填的数字的个数
int row,col,num;  //与后面goin函数有关

//check_row函数是用来查询与空位在相同行上某个数字(不含0)的个数
int check_row(int row,char ch)
{
    int f1=0;
    for(int i=0;i<9;i++){
        if(a[row][i]==ch)f1++;
    }
    return f1;
}

//check_col函数是用来查询与空位在相同列上某个数字(不含0)的个数
int check_col(int col,char ch)
{
    int f2=0;
    for(int i=0;i<9;i++){
        if(a[i][col]==ch)f2++;
    }
    return f2;
}

//check_nine函数是用来查询空位所在九宫格上某个数(不含0)的个数
int check_nine(int i,int j,char ch)
{
    int f3=0;
    for(int x=(i/3)*3;x<(i/3)*3+3;x++)
    for(int y=(j/3)*3;y<(j/3)*3+3;y++)
        if(a[x][y]==ch)f3++;
    return f3;
}

//pos函数是用来搜索某个空位上可能填的数字,并存储下来
void pos(int i,int j,bool flag)
{
    if(flag)printf("第%d行第%d个空的可能答案有",i+1,j+1);
    int r=0;
    for(char x='1';x<='9';x++)
    {
        int f1=check_row(i,x);
        int f2=check_col(j,x);
        int f3=check_nine(i,j,x);
        if(f1>1 || f2>1 || f3>1){cout << "此题无解" <<endl;return;}
        if(!f1 && !f2 && !f3){if(flag)cout << x << ' ';ans[i][j][r++]=x-'0';res[i][j]++;}
    }
    if(flag)cout << endl;
}

bool flag; //定义回溯的标志,初始为false
void dfs(int i,int j)
{
    if(flag)return; //若标志成立,则一直返回
    //若当前位置不是空位,则搜索下一位
    if(a[i][j]!='0'){
        if(j<8)dfs(i,j+1);
        else if(i<8)dfs(i+1,0);
        else if(i==8){flag=true;return;}//
        //若当前位置不是空位,且是最后一位,则标志成立,返回
    }
    //若当前位置是空位,开始搜索
    else{
        //每个空位上的可能填的数的数量为res[i][j]
        for(int x=0;x<res[i][j];x++)
        {
            char ch='0'+ans[i][j][x];
            int f1=check_row(i,ch);//查询相同行上的x的数量
            int f2=check_col(j,ch);//查询相同列上的x的数量
            int f3=check_nine(i,j,ch);//查询所在九宫格上的x的数量
            if(f1>1 || f2>1 || f3>1)return;//若有一处d的数量大于1,返回
            //若当前位置可填x,继续搜索
            if(!f1 && !f2 && !f3){
                a[i][j]=ch;       //填进去
                if(j<8)dfs(i,j+1);//搜索下一位
                else if(i<8)dfs(i+1,0);
                else if(i==8){flag=true;return;}//若正好是最后以为,标志成立,返回
                if(flag)break;//若标志成立,直接break跳出循环并返回
                a[i][j]='0';//若未搜索成功,
            }
        }
    }
    return;
}

//goin函数是用来输入答案的
void goin()
{
    printf("请按照行数+列数+数字的格式输入");
    cin >> row >> col >> num;
}

//goout函数用来,当输入答案正确时,输出现在的sudoku棋盘
void goout(char* p)
{
    for(int i=0;i<9;i++){
        for(int j=0;j<9;j++)
            cout << *(p+9*i+j) <<' ';
        cout << endl;
    }
}

int main(void)
{
    int t1=0,t2=0;        //t1记录空位的数量,t2记录已经填入的空位的数量
    for(int i=0;i<9;i++)cin >> sudoku[i];//输入棋盘
    for(int i=0;i<9;i++)
    for(int j=0;j<9;j++)
    {
        a[i][j]=sudoku[i][j]; //将原来的棋盘复制成a,即确定a的初始状态
        if(sudoku[i][j]=='0')t1++;
    }

    for(int i=0;i<9;i++)
    for(int j=0;j<9;j++)
    if(a[i][j]=='0')pos(i,j,true); //第一遍的时候,输出每个位置上可以填入的数

    dfs(0,0);//深搜答案
    
    printf("正确解法为\n");//输出最终解法
    goout(a[0]);

    while(1){
        //当t2=t1时,表示已经完成,解题成功
        if(t2==t1){
            printf("解题成功,最终答案为\n");
                goout(sudoku[0]);
        }

        goin();//输入解法

        if('0'+num==a[row-1][col-1]){
            printf("正确\n");
            t2++;
            sudoku[row-1][col-1]='0'+num; //填入答案
            goout(sudoku[0]);
        }

        else {
            printf("错误\n");
            goout(sudoku[0]);
        }
    }

}

在这里插入图片描述

在这里插入图片描述

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AC Maker

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值