用C语言做数独

前言数独,相信大家都不陌生,它是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复。数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次。这种取材简单,容易上手的益智小游戏,其热度丝毫不亚于扫雷(今后我还会带来用C语言征服扫雷,感兴趣就多多关注!!)。今天,我们就有学过的C语言知识去征服它。

问题分析:下图就是一个普通的未完成的数独,是个9 x 9的各自图,其中分为9大块,从左往右,从上到下,称第1,2,3,...宫格。如果用坐标来表示每个格子,比如第一行第三个数字6的坐标就为(0,2),第二行第三列没有数字的坐标为(1,2)。那么按照数独的规则,对于坐标(1,2)空格有用的信息就只有与之相同的整一行,整一列以及其所在的宫格,为第二行,第三列以及第一宫格。这些地方出现的数字有(1,2,3,4,6,7),那么这个格子就只能填(5,8,9)其中的一个数字,显然这个格子是信息不足的。那作为一个正常的数独题,一定是会存在那个信息量充足的空格子。比如这题中的坐标为(3,8),与之同一行,同一列以及同一宫格出现的数字有(1,2,3,4,5,7,8,9),所以这个空格就只能填6。

 有了以上分析,首先我们就要获得对于一个坐标格子有用的信息。在此之前呢我们定义一个储存初始数独信息的二维数组,并输入相关信息。代码如下。

main()
{
	int NumAlone[9][9];                     //储存数独
	int i,j;
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			scanf("%d",&NumAlone[i][j]);    //数组赋值
		}
	}
}

接着,我们先获取同一行列的数字信息,并用数组mArray储存起来,其储存的方式是,下标加一对应数字,其值为0,说明未出现该数字,值为1,说明出现了该数字。比如mArray[2]=1,表示数字3出现过,该坐标下的空格也就不能填3。代码如下。

main()
{
	int NumAlone[9][9],mArray[9];         //储存数独和储存信息数组
	int i,j,m,k;
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			scanf("%d",&NumAlone[i][j]);  //数组赋值
		}
		mArray[i]=0;                      //初始化
	}
	line(NumAlone,mArray,1);              //获取同一行数字信息,参数为数独数组,信息数组,横坐标
	row(NumAlone,mArray,3);               //同上,获取同一列数字信息
}

void line(int array[][9],int *p,int n)
{
	for(int i=0;i<9;i++){
		p[array[n][i]-1] = 1;             //信息数组赋值,下同
	}
}

void row(int array[][9],int *p,int n)
{
	for(int i=0;i<9;i++){
		p[array[i][n]-1] = 1;
	}
}

获取行列信息之后,接下来就是获取同一宫格的数字信息,比获取行列数字信息麻烦点。先要明白该坐标处于第几宫格。首先很明显的一点,九个宫格的中心坐标都很清楚,分别为(1,1)(1,4)(1,7)(4,1)(4,4)(4,7)(7,1)(7,4)(7,7)。那么只需要判断该坐标与几个中心坐标的距离是否小于根号二,即可获得其所在九宫格的中心坐标。比如坐标(3,8)和中心坐标(4,7)的距离就刚好等于根号二,故其所在宫格的中心坐标就为(4,7)。那么再来个双重循环,就可以得到该九宫格的数字信息。代码如下

int SudokuPosition(int x,int y)                       //判断九宫格中心坐标位置,参数为空格坐标
{
	int i,j,a,b,result;
	for(i=1;i<=7;i+=3){
		for(j=1;j<=7;j+=3){
			a=abs(x-i);
			b=abs(y-j);
			if(pow(a,2) + pow(b,2) <= 2){             //判断距离
				result=i*10+j;
				break;
			}
		}
	}
	return result;
}

void SquireNine(int array[][9],int *p,int x,int y)   //获取九宫格数字信息
{
	int i,j,xy,a,b;
	xy=SudokuPosition(x,y);                          //中心坐标信息
	a=xy/10-1;b=xy%10-1;                             //信息解析
	for(i=a;i<a+3;i++){
		for(j=b;j<b+3;j++){
			p[array[i][j]-1] = 1;                    //信息数组赋值
		}
	}
}

以上就把所有该获取的数字信息全部获取完毕。接下来就是处理信息,即处理信息数组mArray,判断其含0的个数,如果只有1个,说明信息充足,返回值为0的下标加一,这就是填入该空格子的数字。代码如下

int judgeNum(int *p)        //处理信息数组,参数为信息数组
{
	int flag=0,k;
	for(int i=0;i<9;i++){
		if(p[i]==0){        //判断值为0的个数,并记录其下标
			flag++;
			k=i+1;
		}
	}
	if(flag==1){            //据情况返回值
		return k;
	}else{
		return 0;
	}
}

到这里我们就能给空格子填空了,但是做数独是一个循序渐进的过程,所以我们要不断循环获取某个格子的数字信息,判断其信息是否充足,如果充足,则填入相应的数字,反反复复,每一步都需要用到已经填过的数字。但是程序不能一直跑下去,所以需要一个判断数独是否填完的函数。很简单,代码如下

int Prepare(int Array[][9]}        //判断数独是否填完,参数为数独数组
{
	int i,j,flag=0;
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			if(Array[i][j]==0){
				flag=1;            //如果还有为0的格子,则未填完
			}
		}
	}
	return flag;
}

到这里就解决了所有问题,下面放出完整代码,给出相应的输入输出。

#include<stdio.h>
#include<math.h>
void line(int array[][9],int *p,int n);
void row(int array[][9],int *p,int n);
int judgeNum(int *p);
int SudokuPosition(int x,int y);
void SquireNine(int array[][9],int *p,int x,int y);
void FillBlank(int array[][9],int *p,int x,int y);
int Prepare(int Array[][9]);

main()
{
	int NumAlone[9][9],mArray[9];
	int i,j,m,k;
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			scanf("%d",&NumAlone[i][j]);
		}
		mArray[i]=0;
	}
	//line(NumAlone,mArray,1);
	//row(NumAlone,mArray,3);
	//SquireNine(NumAlone,mArray,1,3);
	//FillBlank(NumAlone,mArray,1,3);
	while(Prepare(NumAlone)){
		for(i=0;i<9;i++){
			for(j=0;j<9;j++){
				if(NumAlone[i][j]==0){
					for(m=0;m<9;m++){
						mArray[m]=0;
					}
					line(NumAlone,mArray,i);
					row(NumAlone,mArray,j);
					SquireNine(NumAlone,mArray,i,j);
					FillBlank(NumAlone,mArray,i,j);
				}
			}
		}
	}
	printf("\n\n");
	for(m=0;m<9;m++){
		for(k=0;k<9;k++){
			printf("%d ",NumAlone[m][k]);
		}
		printf("\n");
	}

	return 0;
}

void line(int array[][9],int *p,int n)
{
	for(int i=0;i<9;i++){
		p[array[n][i]-1] = 1;
	}
}

void row(int array[][9],int *p,int n)
{
	for(int i=0;i<9;i++){
		p[array[i][n]-1] = 1;
	}
}

void SquireNine(int array[][9],int *p,int x,int y)
{
	int i,j,xy,a,b;
	xy=SudokuPosition(x,y);
	a=xy/10-1;b=xy%10-1;
	for(i=a;i<a+3;i++){
		for(j=b;j<b+3;j++){
			p[array[i][j]-1] = 1;
		}
	}
}

int judgeNum(int *p)
{
	int flag=0,k;
	for(int i=0;i<9;i++){
		if(p[i]==0){
			flag++;
			k=i+1;
		}
	}
	if(flag==1){
		return k;
	}else{
		return 0;
	}
}

int SudokuPosition(int x,int y)
{
	int i,j,a,b,result;
	for(i=1;i<=7;i+=3){
		for(j=1;j<=7;j+=3){
			a=abs(x-i);
			b=abs(y-j);
			if(pow(a,2) + pow(b,2) <= 2){
				result=i*10+j;
				break;
			}
		}
	}
	return result;
}

void FillBlank(int array[][9],int *p,int x,int y)
{
	array[x][y]=judgeNum(p);
}

int Prepare(int Array[][9])
{
	int i,j,flag=0;
	for(i=0;i<9;i++){
		for(j=0;j<9;j++){
			if(Array[i][j]==0){
				flag=1;
			}
		}
	}
	return flag;
}

 

 

 以上就是我要分享的全部内容了,觉得还不错的话,可以多点赞哟!

  • 29
    点赞
  • 117
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,这里提供一个简单的 C 语言代码示例,可以生成和解决数独。代码中使用了回溯算法求解数独,实现方法与前面提供的代码示例类似。 ```c #include <stdio.h> #include <stdlib.h> #include <time.h> // 定义数独的大小 #define N 9 // 定义数独 int sudoku[N][N] = { {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0} }; // 判断某个数是否可以填入某个位置 int can_place(int row, int col, int num) { // 判断当前行是否有相同数字 for (int i = 0; i < N; i++) { if (sudoku[row][i] == num) { return 0; } } // 判断当前列是否有相同数字 for (int i = 0; i < N; i++) { if (sudoku[i][col] == num) { return 0; } } // 判断当前子矩阵是否有相同数字 int sub_row = (row / 3) * 3; // 子矩阵的起始行号 int sub_col = (col / 3) * 3; // 子矩阵的起始列号 for (int i = sub_row; i < sub_row + 3; i++) { for (int j = sub_col; j < sub_col + 3; j++) { if (sudoku[i][j] == num) { return 0; } } } return 1; } // 随机填入数字 int fill_random(int row, int col) { if (row >= N) { return 1; } int arr[N] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; for (int i = 0; i < 9; i++) { int j = rand() % 9; int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } for (int i = 0; i < N; i++) { int num = arr[i]; if (can_place(row, col, num)) { sudoku[row][col] = num; if (col < N - 1) { if (fill_random(row, col + 1)) { return 1; } } else { if (fill_random(row + 1, 0)) { return 1; } } sudoku[row][col] = 0; } } return 0; } // 求解数独 int solve_sudoku(int row, int col) { // 如果当前行和列都大于等于 N,说明已经解出数独 if (row >= N && col >= N) { return 1; } // 如果当前列大于等于 N,换行 if (col >= N) { row++; col = 0; } // 如果当前位置已经填有数字,跳过 if (sudoku[row][col] != 0) { return solve_sudoku(row, col + 1); } // 尝试填入数字 for (int i = 1; i <= N; i++) { if (can_place(row, col, i)) { sudoku[row][col] = i; // 递归求解下一个位置 if (solve_sudoku(row, col + 1)) { return 1; } // 如果无解,回溯 sudoku[row][col] = 0; } } // 如果所有数字都不能填入当前位置,无解 return 0; } // 输出数独 void print_sudoku() { for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) { printf("%d ", sudoku[i][j]); } printf("\n"); } } int main() { // 随机生成数独 srand((unsigned int) time(NULL)); fill_random(0, 0); // 输出数独 printf("随机生成的数独为:\n"); print_sudoku(); // 求解数独 if (solve_sudoku(0, 0)) { // 输出结果 printf("数独的解为:\n"); print_sudoku(); } else { // 无解 printf("无解!\n"); } return 0; } ``` 这个代码可以随机生成数独,并使用回溯算法求解数独。生成数独的方法与前面提供的代码示例类似,不同的是它是随机填入数字,而不是从输入中读取数字。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值