关于如何求解数独问题

来源:互联网 发布:淘宝企业店铺税收问题 编辑:程序博客网 时间:2024/06/08 04:27

在华为OJ平台上做到了一道这样的题目,编写一个输入数独题目,输出数独求解结果的小程序,下面是编写的过程。
idea1
最先想到求解数独的方法是这样的
1、遍历9行*9列的每个未知数,对它所在的行、列、块进行遍历,如果可以通过此确定唯一的一个数字,那么就可以得出它的值。
2、然后对下一个数字的行、列、块进行遍历,查找它的值,如果有两个或者更多可能的值,则只能跳过到下一个数字。
3、不断循环遍历这9行9列中的未知数,直到查找到所有的数字。
这里写图片描述

编写完成了,代码如下:

#include<iostream>#include<string>using namespace std;class Point{public:    int value; //九宫格的值    int num[10];//取1到9的可能性,如果没可能取1,则令a[1]=0    int number;    Point(){        num={0,1,1,1,1,1,1,1,1,1};        number=0;        value =0;    }};int main(){    Point p[9][9];    //初始化p[9][9]    int i,j;    for(i=0;i<9;i++){        for(j=0;j<9;j++){            cin>>p[i][j].value; //输入i行j列        }    }    int k,l;    int temp;    int run=true;    int cycleCount=0;    //循环查81个点多次,直到全部查出    while(run){        if(cycleCount>100){            cout<<"做不出来( ⊙ o ⊙ )!"<<endl;break;        }        cycleCount++;        run = false;        //遍历9*9=81个点        for(i=0;i<9;i++){            for(j=0;j<9;j++){                if(p[i][j].value==0){ //如果该空的值未知                    //行向                    for(k=0;k<9;k++){                        if(k!=j){                            p[i][j].num[p[i][k].value]=0;                        }                    }                    //列向                    for(k=0;k<9;k++){                        if(k!=i){                            p[i][j].num[p[k][j].value]=0;                        }                    }                    //块向                    for(k=(i/3)*3;k<((i/3)*3+3);k++)                        for(l=(j/3)*3;l<((j/3)*3+3);l++){                            if(k!=i&&l!=j){                                p[i][j].num[p[k][l].value]=0;                            }                    }                    //判断是否可读出                    for(k=1;k<10;k++){                        if(p[i][j].num[k]==1){                            p[i][j].number++;                            temp=k; //暂存value                        }                    }                    if(p[i][j].number==1){                        p[i][j].value = temp;                    }else if(p[i][j].number>1){                        p[i][j].number=0;                        run=true;//还存在空格,所以不能停止                    }else if(p[i][j].number==0){                        //cout<<"输入错误!";//getchar();                    }                }            }        }    }    //输出结果    for(i=0;i<9;i++){        for(j=0;j<9;j++)           {cout<<p[i][j].value<<" ";}            cout<<endl;    }    return 0;}

其实这样的程序应对平台上的测试样例还是OK的,但是细思极恐,这样的程序真的能克服所有的数独题目吗?
上网找了一些难度比较大的数独题目输入后,程序就无解了,我认定循环遍历9宫格超过100次,问题就肯定无法通过这样的方式解决。

但是对于难度大的数独题目怎么求解呢?
为了找到编程求解的方法,我找了一道数独题目尝试做一下看看。

我发现可以通过另一种方式直接得出某空要填写什么数字

idea2
查找每列、每列、每一块未知数的可能要填写的数字,如果某种可能的数字只在该行、该列或者该块仅出现一次,则该种可能是必然。
例如:
4 2 5 1 0 0 3 0 0
3 8 9 0 0 0 2 0 1
1 6 7 3 2 9 4 8 5
7 0 8 0 0 0 6 0 9
9 0 6 0 7 0 8 0 0
2 0 1 0 0 0 5 0 0
8 9 3 7 5 6 1 4 2
5 1 2 0 0 0 7 0 0
6 7 4 0 0 1 9 5 0
对于第1行第5列,直观看过去它可能取6,8
第1行第6列可能取7,8
第1行第7列可能取6,7,9
第1行第9列可能取6,7
综上可以发现,9只在第1行第7列的可能性中出现了1次,所以第1行第7列的数字一定是9
这种判断方式可以通过一行一列或者一块去查找。

利用这种方法我们再修改一下原来的程序
加上一个补充算法,整体代码如下

#include<iostream>#include<string>#include<stdio.h>using namespace std;class Point{public:    int value; //九宫格的值    int num[10];//取1到9的可能性,如果没可能取1,则令a[1]=0    int number;    Point(){        num={0,1,1,1,1,1,1,1,1,1};        number=0;        value=0;    }};    Point p[9][9];    //初始化p[9][9]void fun(){    int i1,j1,k1,l1;    for(i1=0;i1<9;i1++){            for(j1=0;j1<9;j1++){                if(p[i1][j1].value==0){                    //行向                    for(k1=0;k1<9;k1++){                        if(k1!=j1){                            p[i1][j1].num[p[i1][k1].value]=0;                        }                    }                    //列向                    for(k1=0;k1<9;k1++){                        if(k1!=i1){                            p[i1][j1].num[p[k1][j1].value]=0;                        }                    }                    //块向                    for(k1=(i1/3)*3;k1<((i1/3)*3+3);k1++)                        for(l1=(j1/3)*3;l1<((j1/3)*3+3);l1++){                            if(k1!=i1&&l1!=j1){                                p[i1][j1].num[p[k1][l1].value]=0;                            }                    }                }            }        }}int main(){    int i,j;    int k,l;    int temp;    int run=true;    int cycleCount=0;    for(i=0;i<9;i++){        for(j=0;j<9;j++){            cin>>p[i][j].value; //输入i行j列        }    }    //循环查81个点多次,直到全部查出    while(run){        //死循环判断        if(cycleCount>10000){            cout<<"做不出来( ⊙ o ⊙ )!"<<endl;break;        }        cycleCount++;        run = false;        /*算法1*********************************************************/        //遍历9*9=81个点        for(i=0;i<9;i++){            for(j=0;j<9;j++){                if(p[i][j].value==0){ //如果该空的值未知                    //行向                    for(k=0;k<9;k++){                        if(k!=j){                            p[i][j].num[p[i][k].value]=0;                        }                    }                    //列向                    for(k=0;k<9;k++){                        if(k!=i){                            p[i][j].num[p[k][j].value]=0;                        }                    }                    //块向                    for(k=(i/3)*3;k<((i/3)*3+3);k++)                        for(l=(j/3)*3;l<((j/3)*3+3);l++){                            if(k!=i&&l!=j){                                p[i][j].num[p[k][l].value]=0;                            }                    }                    //判断是否可读出                    for(k=1;k<10;k++){ //遍历数字1-9                        if(p[i][j].num[k]==1){                            p[i][j].number++;                            temp=k; //暂存value                        }                    }                    if(p[i][j].number==1){                        p[i][j].value = temp; /*赋给空的格子一个值时,要调用fun方法*/                        fun();                        cout<<"主程序算法--"<<i+1<<"行"<<j+1<<"列"<<" "<<"值:"<<temp<<endl;                    }else if(p[i][j].number>1){                        p[i][j].number=0;                        run=true;//还存在空格,所以不能停止                    }else if(p[i][j].number==0){                        //cout<<"输入错误!";//getchar();                    }                }            }        }        /*算法2*********************************************************/        /*        查找每列、每列、每一块未知数的可能要填写的数字,如果某种可能的数字只在该行、该列或者该块        仅出现一次,则该种可能是必然        */        //查找行        int aim[10];  //记录1-9一一对应的所在的列        int count[10]; //记录1-9在该行的可能出现的次数        for(i=0;i<9;i++){ //行            for(j=0;j<9;j++){                if(p[i][j].value==0){ //修复bug                    for(k=1;k<=9;k++){                        if(p[i][j].num[k]==1){ //数字k在i行j列可能存在                            count[k]++;  //数字k可能的数字加一                            if(count[k]==1){                                aim[k] = j; //数字k对应j列                            }else if(count[k]>1){                                aim[k] = 0; //非一一对应了                            }                        }                    }                }            }            for(k=1;k<=9;k++){ //遍历1-9这9个数字                if(count[k]==1){ //如果数字k只出现一次                    p[i][aim[k]].value = k;                    fun();                    cout<<"补丁算法--行向--"<<i+1<<"行"<<aim[k]+1<<"列"<<" "<<"值:"<<k<<endl;                }            }            aim = {0,0,0,0,0,0,0,0,0,0};            count={0,0,0,0,0,0,0,0,0,0};        }        //查找列        for(j=0;j<9;j++){ //列            for(i=0;i<9;i++){ //行                if(p[i][j].value==0){                    for(k=1;k<=9;k++){ //遍历数字1-9                        if(p[i][j].num[k]==1){ //数字k在i行j列可能存在                            count[k]++;                            if(count[k]==1){                                aim[k] = i; //数字k对应i行                            }else if(count[k]>1){                                aim[k] = 0; //非一一对应了                            }                        }                    }                    /*测试*/                    /*                    if((j+1)==7){ //如果在第7列                        cout<<"遍历第7列"<<",第"<<i+1<<"行"<<endl;                        for(k=1;k<10;k++)                        {cout<<"数字"<<k<<"的出现次数:"+count[k]<<endl;}                    }*/                }            }            for(k=1;k<=9;k++){                if(count[k]==1){                    p[aim[k]][j].value = k;                    fun();                    cout<<"补丁算法--列向--"<<aim[k]+1<<"行"<<j+1<<"列"<<" "<<"值:"<<k<<endl;                }            }            aim = {0,0,0,0,0,0,0,0,0,0};            count={0,0,0,0,0,0,0,0,0,0};        }        int aimRow[10];        int aimCol[10];        int m;        //查找块        for(i=0;i<3;i++)            for(j=0;j<3;j++){ //9个大块                for(m=i*3;m<i*3+3;m++)                    for(l=j*3;l<j*3+3;l++){ //一大块有9小块                    if(p[k][l].value==0){                        for(k=1;k<=9;k++){ //遍历数字1-9                            if(p[i][j].num[k]==1){ //数字k在i行j列可能存在                                count[k]++;                                if(count[k]==1){                                    aimRow[k] = m;                                    aimCol[k] = l;                                }else if(count[k]>1){                                    aimRow[k] = 0;                                    aimCol[k] = 0;                                }                            }                        }                    }                }                //进行取值            for(k=1;k<=9;k++){                if(count[k]==1){                    p[aimRow[k]][aimCol[k]].value = k;                    fun();                    cout<<"补丁算法--块向--"<<aimRow[k]+1<<"行"<<aimCol[k]+1<<"列"<<" "<<"值:"<<k<<endl;                }            }            aimRow = {0,0,0,0,0,0,0,0,0,0};            aimCol = {0,0,0,0,0,0,0,0,0,0};            count={0,0,0,0,0,0,0,0,0,0};        }    }    //输出结果    for(i=0;i<9;i++){        for(j=0;j<9;j++)           {cout<<p[i][j].value<<" ";}            cout<<endl;    }    return 0;}

从上面的代码可以看出来,在每次为一个空格找到它的value时,我都执行了一个fun方法,fun方法是遍历9*9个空格,将未知空格的num[10](记录1-9的可能性)更新一下。不及时更新会出很多乱子。

其实到这里还远远没有结束,这样还是不能解出许多数独题目,正常做数独题目时要用到假设会比较快的解决一道数独题目,也许在这里尝试这种方法会比较好,在数独这里耗的时间比较多了,下次有兴趣再接着解决吧。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 伊苏8食谱少了怎么办 u盘图标隐藏了怎么办 u盘图标不显示怎么办 u盘文件夹被隐藏怎么办 u盘恶意隐藏文件怎么办 结婚7年没孩子怎么办 htc手机时间变了怎么办 下的视频没声音怎么办 ipad很卡反应慢怎么办 微信视频声音卡怎么办 苹果6s没有声音怎么办 靠上班工资太低怎么办 楼上太吵怎么办能报警 转页风扇有噪音怎么办 住的地方太吵怎么办 租的房子太吵怎么办 苹果手机音量键坏了怎么办 xp系统声音没了怎么办 柿子烂到窗台上怎么办 小窗户厨房太暗怎么办 抬东西把腰闪了怎么办 搬重东西后腰疼怎么办 闪了腰怎么办一动就疼 窗户的把手断了怎么办 窗户寸漏不了水怎么办 窗户打开关不上怎么办 新装修的房子有甲醛怎么办 橄榄核上油花了怎么办 虫子飞到耳朵里怎么办 手被虫子咬肿了怎么办 梦见牙掉出血该怎么办 黑户急需5万块钱怎么办 家里欠了好多钱怎么办 欠好多网贷我该怎么办 外面欠了很多钱怎么办 欠了好多网贷怎么办 欠那么多钱我该怎么办 急用钱怎么办谁给指条路 晚上睡不着觉怎么办白天又醒不来 胃疼了好几天怎么办 手机移动卡怎么办副卡