数独 ( 三 ) ——解数独

数独 ( 三 ) ——解数独

我的地址:https://github.com/gmj0301/gmj
请翻看前面的博客,有细节上的修改。

解题思路

使用深度优先搜索方法,该过程简单。但是当求解题目数量达到很大时,所用时间很长。剪枝是很重要的一个环节。

剪枝过程

对于数独终局的每一个位置,都代表递归过程中的一层,每次在当前位置填入一个数字后,通过遍历当前行、当前列、以及当前 3 × 3 网格中的数字,就可以排除不符合条件的数字,从而达到剪枝的目的。
剪枝的过程其实已经完成了对当前数独的合法性的检查。

函数设计

整体设计实现过程如下:
在这里插入图片描述
函数设计如下:

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <string.h>
int sudo[9][9];
int judge_all = 0, num_1 = 0;


void write(); //把解写入数独答案文件

bool judge(int h, int l); //剪枝函数,判断当前位置是否能放该数字

void solve_sudo(int i, int j); //深度优先搜素

void solve(const char* txt); //读数独题目文件,解数独,写入

主要的函数是 judge 和 solve_sudo 这两个函数,其设计和代码如下:

judge函数

在这里插入图片描述
当把数字填入蓝色的空时,填入的数字,要考虑是否与黄色部分重复,重复不能填入。
机器不能像人一样,可以在填入之前就可直接判断出是否重复,需要选定一个数字填入后再判断。
判断过程有三步:

  1. 该数字所处行是否有重复,如果有,返回 false;
  2. 该数字所处列是否有重复,如果有,返回 false;
  3. 该数字所处九宫格是否有重复,如果有,返回 false;

在判断第 3 步时,需找到九宫格的数组下标范围。
在设计时,我的存储数独的数组下标本身比他的个数少 1,需先除以 3 确定是第几个行或列,再乘以 3 确定该九宫格的第一行,加 2 确定最后行。
如,检查下标为 [ 0, 2 ] 位置的数字,0 ÷ 3 = 0,0 × 3 = 0,0 + 2 = 2,第一个九宫格的行数由 0 到 2。
2 ÷ 3 = 0,0 × 3 = 0,0 + 2 = 2,第一个九宫格的列数由 0 到 2。
代码如下:

bool judge(int h, int l) {
	for (int i = 0; i < 9; i++)
		if (sudo[i][l] == sudo[h][l] && i != h)       //如果同一列存在与当前位置相同的数字,则出错
			return false;
	for (int i = 0; i < 9; i++)
		if (sudo[h][i] == sudo[h][l] && i != l)       //如果同一行存在与当前位置相同的数字,则出错
			return false;
	int x = (h / 3) * 3;
	int y = (l / 3) * 3;
	for (int i = x; i < x + 3; i++)
		for (int j = y; j < y + 3; j++)
			if (sudo[h][l] == sudo[i][j] && i != h && j != l)       //如果同一九宫格存在与当前位置相同的数字,则出错
				return false;
	return true;       //没有出现上述错误,则返回true
}
深度优先搜索函数 solve_sudo

在当前位置分别填入数字 1 - 9 ,填入数字后,使用 judge 函数判断,如果返回值为 true,搜索找到下个没填数字的位置,调用 solve_sudo 函数。如果数字 1 - 9 依次填过后都返回false,说明此位置没有符合的数字,是上一个填入的数字出错导致的,当前位置置 0(防止被认为当前位置不需要填入数字,影响后续)后返回上一个位置。
solve_sudo 函数内部的实现过程如下:
在这里插入图片描述
代码如下:

void solve_sudo(int i, int j) {
	if (judge_all == 1) return;

	int k, h, l, temp;
	for (k = sudo[i][j] + 1; k <= 9; k++) {
		sudo[i][j] = k;
		if (judge(i, j)) {    //当前位置可以填入数字i

			h = i;            //记录下当前位置旁边的下标[h,l]
			l = j + 1;
			temp = 0;
			for (; h < 9; h++) {
				for (; l < 9; l++)
					if (sudo[h][l] == 0) {   //如果[h,l]处数字为0
						temp = 1;            //temp=1标记
						break;
					}
				if (temp == 1) break;
				else l = 0;
			}
			if (temp == 1) solve_sudo(h, l);  //[h,l]处数字为0,调用solve_sudo函数
			else {
				judge_all = 1;              //所有位置都不为0,说明数独题目解完了
				return;                     //返回
			}
			if (judge_all == 1) return;     //返回
		}

	}
	//1-9这9个数字都填完了,没有符合的数字
	//说明上一个位置出的数字填错了,当前位置置0,回到上一个位置
	sudo[i][j] = 0;
	return;
}

写入文件时是一个字符一个字符写入的。
代码如下:

void write()
{
	FILE* fp;
	if (num_1 == 0)	fp = fopen("solve_sudo.txt", "w");   //当前是第一题的解,需要清空文件里面的所有内容                                
	else fp = fopen("solve_sudo.txt", "a+"); //当前不是第一题的解,需要保存文件里面的所有内容,在其基础上再写入新的解
	if (num_1 != 0) fprintf(fp, "\n");           //除了第一题,每一题都要先输出一个空格符,再输出其解,与前面的答案分开
	char s[18];
	int k;
	for (int i = 0; i < 9; i++) {
		for (int j = 0; j < 9; j++) {
			k = j * 2;
			s[k] = sudo[i][j] + '0';
			s[k + 1] = ' ';
		}
		s[17] = '\0';
		fputs(s, fp);
		fprintf(fp, "\n");
	}
	fclose(fp);
	if (num_1 == 0) num_1++;         //表明这不是第一题了
}

相关测试放到下一篇博客里,请查看下一篇博客。
因为最开始更博客时,有些函数不完善,所以博客有更改。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要使用Python数独,可以按照以下步骤进行操作: 1. 首先,将待数独转换成Python矩阵。可以使用Python中的列表来表示数独的矩阵结构。 2. 接下来,可以使用递归的方尝试数独。递归是一种重复调用函数自身的方,可以用于数独问题。 3. 在递归函数中,可以编写代码来判断数独是否已经出。可以检查每个格子是否已经填满,并且保证每一行、每一列和每个小九宫格中的数字都不重复。 4. 如果数独出,则需要使用回溯。回溯是一种试错的方,当某一步操作导致数独时,需要返回上一步并尝试其他可能的数字。 5. 通过递归和回溯的不断尝试,直到将数独出为止。 以下是一个示例代码的框架,可以参考来编写Python数独的程序: ```python def solve_sudoku(board): if is_sudoku_solved(board): return True # 找到一个空格子 row, col = find_empty_cell(board) for num in range(1, 10): if is_valid_move(board, row, col, num): board[row][col = num if solve_sudoku(board): return True board[row][col = 0 return False def is_sudoku_solved(board): # 检查数独是否已出 pass def find_empty_cell(board): # 找到一个空格子 pass def is_valid_move(board, row, col, num): # 判断当前数字是否可以放置在指定位置 pass # 主函数 def main(): # 初始化数独矩阵 board = [ [5, 3, 0, 0, 7,0, 0, 0, 0], [6, 0, 0, 1, 9, 5, 0, 0, 0], [0, 9, 8, 0, 0, 0, 0, 6, 0], [8, 0, 0, 0, 6, 0, 0, 0, 3], [4, 0, 0, 8, 0, 3, 0, 0, 1], [7, 0, 0, 0, 2, 0, 0, 0, 6], [0, 6, 0, 0, 0, 0, 2, 8, 0], [0, 0, 0, 4, 1, 9, 0, 0, 5], [0, 0, 0, 0, 8, 0, 0, 7, 9] ] # 数独 solve_sudoku(board) # 打印出的数独矩阵 for row in board: print(row) if __name__ == '__main__': main() ``` 通过以上步骤和示例代码,可以使用Python数独问题。注意,以上代码只是一个框架,具体的实现细节需要根据实际需求进行修改和完善。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [6个步骤教你用Python数独!(含实例代码)](https://blog.csdn.net/weixin_41334453/article/details/102805525)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值