C语言数独游戏的求解方法

所属分类: 软件编程 / C 语言 阅读数: 112
收藏 0 赞 0 分享

数独游戏的解法:

先将数独分为九个格子,用一个数组将每个小九宫格的候选数存放下来,将候选数挨个放进数独里的空位,如果这一行和这一列都没有这个数字,继续放入下一个,如果不能放入的话就回到上一步继续尝试,直到成功求出数独的解为止;

比如这个数独第一个九宫格的候选数就有1,2,7,8,9,我们需要从1开始放入第一个格子挨个尝试直到8的时候发现剩下的两个格子都不能放入

这个时候我们就要撤回上一个插入的7,发现8仍然不能放入,就继续撤回2,发现8可以放入,就将8放入3号位置,然后将9插入

这个时候我们发现2不能放入剩下的两格,我们就继续撤回到1插入的时候,将2放入1号位置,然后挨个放入剩下的数

循环这一过程,直到数独求出解为止;

这个方法比较容易想到,操作也比较容易实现

下面是代码

代码大多数都写了备注便于理解

题目需要的1000道题放在下面了,将这1000个txt文件拷到EXE文件同一目录就可以了

题目链接:数独题目

#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
#define MAX 81
typedef struct asd{
 int x;//待测试的值的x坐标 
 int y;//待测试的值的y坐标 
 int p;//待测试的值的位置(1道9代表在九宫格里的位置) 
 int n;//待测试的值
}A;
A zhan[MAX];//存放每个放进题目数组测试的数据 
void kongque(int queshi[9][9],int aa[9][9]);//函数将候选数数组里去除题目中有的数字 
void shuchu(int aa[9][9],int q);//输出整个数组到文件中 
int end(int aa[9][9]); //判断是否结束 
int next(int queshi[9][9],int m,int n,int *x,int *y,int aa[9][9]);//查找下一个应该放进九宫格测试的数据 
int chazhao(int aa[9][9],int m,int n,int num);//查找同一行同一列是否有相同的值 
int nfrz(int queshi[9][9],int aa[9][9],int m,int n,int *p);//判断是否满足入栈条件(就是当前值是否可以插入九宫格) 
int rz(int *t,int x,int y,int p,int num);//入栈操作 
int cz(int *t,int *x,int *y,int *p,int *num);//出栈操作 
void aaaa(char aa[10],int a);//计算题目文件的文件名 
void bbbb(char aa[10],int a);//计算答案文件的文件名
int main(){
 int i;//记录该调用哪道题 
 for(i=0;i<1000;i++){
 int aa[9][9],j,k;//aa数组存放的是题目数独 
 int queshi[9][9];//存放的是每个九宫格的待选数 
 int end=0;//判断循环结束条件 
 int h=0,l=0,p=1;//h是候选数的行坐标,l是候选数的列坐标,p代表当前测试数属于小九宫格的位置 
 int t=-1;//栈的长度 
 int s=0,num;
 FILE *u;
 char qwe[10];
 for(j=0;j<9;j++)//将数组置为每行都是(1到9) 
 for(k=0;k<9;k++)
 queshi[j][k]=k+1;
 aaaa(qwe,i);
 u=fopen(qwe,"r");
 for(j=0;j<9;j++){//读入题目 
 for(k=0;k<9;k++){
 fscanf(u,"%d",&aa[j][k]);
 }
 }
 fclose(u);
 memset(zhan,0,sizeof(zhan));//将栈的数据全部置为0 
 kongque(queshi,aa);
 while(end!=1){//开始求解 
 s=next(queshi,h,l,&h,&l,aa);//查找下一个应该放进九宫格测试的数据
 if(s==0){//如果找到则进入下一层 
 s=nfrz(queshi,aa,h,l,&p);//判断能否插入数独里 
 if(s==0){//如果可以则将插入的数据存放到栈里(入栈) 
  s=rz(&t,h,l,p,queshi[h][l]);
  if(s==0){ //如果入栈成功则写入数独
  aa[h/3*3+(p-1)/3][h%3*3+(p-1)%3]=queshi[h][l];
  l++;//待选数跳到下一个 
  p=1;//重新从第一个小格子开始判断是否插入 
  }
  else{
  end=1;//循环结束 
  }
 }
 else{
  s=cz(&t,&h,&l,&p,&num);
  if(s==0){//如果出栈成功则擦除插入的数据 
  aa[h/3*3+(p-1)/3][h%3*3+(p-1)%3]=0;
  p++;
  }
  else
  end=1;
 }
 }
 else if(s==-1){
 shuchu(aa,i);//输出求解完毕的数独 
 end=1; 
 }
 else{
 printf("发生未知错误");
 end=1;
 }
 }
 }
 return 0;
}
//函数将候选数数组里去除题目中有的数字 
void kongque(int queshi[9][9],int aa[9][9]){
 int i,j,x,y;
 for(i=0;i<j;i++){
 for(j=0;j<9;j++){
 if(aa[i][j]){
 x=i/3*3+j/3;//数独数组和候选数数组的坐标转换 
 y=aa[i][j]-1;
 queshi[x][y]=0;
 }
 }
 }
}
//输出整个数组到文件中
void shuchu(int aa[9][9],int q){
 int i,j;
 FILE *p;
 char qq[10];
 bbbb(qq,q);
 p=fopen(qq,"w");
 for(i=0;i<9;i++){
 for(j=0;j<9;j++){
 fprintf(p,"%d ",aa[i][j]);
 }
 fprintf(p,"\n");
 }
 fclose(p);
}
//判断是否结束
int end(int aa[9][9]){
 int i,j,num=0;
 for(i=0;i<9;i++){
 num=0;
 for(j=0;j<0;j++){
 num+=aa[i][j];//检查每一行是否为1到9 
 }
 if(num!=45)
 return -1;
 }
 for(j=0;j<9;j++){//检查每一列是否为1到9 
 num=0;
 for(i=0;i<9;i++){
 num+=aa[i][j];
 }
 if(num!=45)
 return -1;
 }
 return 0;
}
//查找下一个应该放进九宫格测试的数据 
int next(int queshi[9][9],int m,int n,int *x,int *y,int aa[9][9]){
 int qqq=0;
 if(n>8){//如果当前小九宫格填写完毕则进入下一个九宫格 
 n=0;
 m++;
 }
 if(m>8){
 qqq=end(aa);//判断是否结束 
 if(qqq!=0)
 return -1;
 else
 return 1;
 }
 while(queshi[m][n]==0){
 if(n<8)
 n++;
 else{
 n=0;
 m++;
 if(m>8){
 qqq=end(aa);
 if(qqq!=0)
  return -1;
 else
  return 1;
 }
 }
 }
 *x=m;//重新获取测试的值的x坐标和y坐标 
 *y=n;
 return 0;
}
//查找同一行同一列是否有相同的值 
int chazhao(int aa[9][9],int m,int n,int num){
 int i;
 for(i=0;i<9;i++){//查找行 
 if(aa[m][i]==num)
 return -1;
 }
 for(i=0;i<9;i++){//查找列 
 if(aa[i][n]==num)
 return -1;
 }
 return 0;
}
//判断是否满足入栈条件(就是当前值是否可以插入九宫格) 
int nfrz(int queshi[9][9],int aa[9][9],int m,int n,int *p){
 int s=*p;
 int i,t1,t2,num;
 num=queshi[m][n];
 for(i=s;i<10;i++){
 t1=(m/3)*3+(s-1)/3;
 t2=(m%3)*3+(s-1)%3;
 if(aa[t1][t2]!=0){
 s++;
 continue;
 }
 if(chazhao(aa,t1,t2,num)!=0){
 s++;
 continue;
 }
 else{
 *p=s;
 return 0;
 }
 }
 return -1;
}
//入栈操作 
int rz(int *t,int x,int y,int p,int num){
 if(*t>=MAX){
 return -1;
 }
 else{
 (*t)++;
 zhan[*t].x=x;
 zhan[*t].y=y;
 zhan[*t].p=p;
 zhan[*t].n=num;
 return 0;
 }
}
//出栈操作 
int cz(int *t,int *x,int *y,int *p,int *num){
 if(*t==-1){
 return -1;
 }
 else{
 *x=zhan[*t].x;
 *y=zhan[*t].y;
 *p=zhan[*t].p;
 *num=zhan[*t].n;
 (*t)--;
 return 0;
 }
}
//计算题目文件的文件名 
void aaaa(char aa[10],int a){
 if(a>=0&&a<10){
 aa[0]='0';
 aa[1]='0';
 aa[2]='0';
 aa[3]=a+'0';
 }
 else if(a<100){
 aa[0]='0';
 aa[1]='0';
 aa[2]=a/10+'0';
 aa[3]=a%10+'0';
 }
 else if(a<1000){
 aa[0]='0';
 aa[1]=a/100+'0';
 aa[2]=a/10%10+'0';
 aa[3]=a%10+'0';
 }
 aa[4]='.';
 aa[5]='t';
 aa[6]='x';
 aa[7]='t';
 aa[8]='\0';
}
//计算答案文件的文件名
void bbbb(char aa[10],int a){
 if(a>=0&&a<10){
 aa[0]='a';
 aa[1]='0';
 aa[2]='0';
 aa[3]=a+'0';
 }
 else if(a<100){
 aa[0]='a';
 aa[1]='0';
 aa[2]=a/10+'0';
 aa[3]=a%10+'0';
 }
 else if(a<1000){
 aa[0]='a';
 aa[1]=a/100+'0';
 aa[2]=a/10%10+'0';
 aa[3]=a%10+'0';
 }
 aa[4]='.';
 aa[5]='t';
 aa[6]='x';
 aa[7]='t';
 aa[8]='\0';
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多