80ms 求解世上最难数独 —— DFS的灵活运用

最近写了好几道DFS相关的题目,想起以前玩过的一个游戏:数独,因为都是一个类型的思想,所以很快就想到了用 DFS来求解 数独,此文章来教你一步一步来实现一个数独模拟器 . . .
.
相关文章:
蓝桥杯DFS经典题 —— 算式900、 寒假作业(告别枚举法)


我们需要求解的数独就是世界上最难的数独:
在这里插入图片描述

这个数独被当时一个 69岁的爷爷花了三天时间给算出了,有这样一个故事:
在这里插入图片描述

下面我将带大家手把手写出这样的一个小程序,来快速的求解出世上最难的数独 . . .

首先,我们都知道数独是有规则的,上面的数据不是随便放的,规则如下:

  1. 一行九个数据,9个数字必须出现
  2. 一列九个数据,9个数字必须出现
  3. 一个九宫格,9个数字必须出现

比如下面这个情况才是允许存在的:
在这里插入图片描述

所以,我们需要写一个方法,用来判断当前的这个数字是否允许存放在这个数独之中,代码如下:

bool JudgeIsNoWant(int n)	// n 表示是当前的第几个数
{
    int x = n / 9;  		// 定位当前数的位置(数据在二维数组中的位置)
    int y = n % 9;
  
    for (size_t i = 0; i < 9; i++)  // 横竖 搜索
    {
  	if (arr[x][i] == arr[x][y] && i != y) return false;
  	if (arr[i][y] == arr[x][y] && i != x) return false;
    }
  
    // 九宫格 搜索	这里需要一个小小的算法,确定九宫格的位置
    for (size_t i = x / 3 * 3; i < x / 3 * 3 + 3; i++)
  	for (size_t j = y / 3 * 3; j < y / 3 * 3 + 3; j++)
   	    if (arr[i][j] == arr[x][y] && (i != x || j != y))
    		return false;
    
    return true;
}

其中的九宫格位置确定算法如下所示:
在这里插入图片描述
其中,这个 X 的值的位置是 (4,5),九宫格起始位置是(3,3),我们需要设计一个算法来打出这个点,而这个算法就是上面代码上所述的:在这里插入图片描述

  • 4 / 3 * 3 = 3
  • 5 / 3 * 3 = 3

.

下面我们就可以使用 DFS 来求解这个世上最难的数独了,代码如下所示:

void Dfs(int n)  // 深搜思想
{
  // 判断上一个数据是否满足条件( 横、竖、九宫格 )
    if (n > 0 && n <= 81) if (!JudgeIsNoWant(n - 1)) return;
    
    if (n >= 81) 	// 到最后则输出数据并且返回
    {
  	cout << endl << endl << "解为:" << endl << endl;
  	for (size_t i = 0; i < 9; i++){
   	    for (size_t j = 0; j < 9; j++)
   	    { 
    		cout << arr[i][j] << " "; 
    		if (j % 3 == 2) cout << " ";
   	    }
   	    cout << endl;
   	    if (i % 3 == 2) cout << endl;
  	}
  	return;
    }
    
    int x = n / 9;  // 定位当前数的位置
    int y = n % 9;
    
    if (arr[x][y] != 0)		// 只对 数字为 0 的元素进行搜索
  	Dfs(n + 1);
    else {
    	// 每一个空位有 10 种可能性
    	for (size_t i = 1; i < 10; i++) {
   	    arr[x][y] = i;	// 当前的数字放入到数组之中,
   	    Dfs(n + 1);		// 进行下一个位置的搜索
   	    arr[x][y] = 0;	// 不满足条件,重新清 0
  	}
    }
}

这里的关键点在于这一行代码:
在这里插入图片描述

为什么我们需要判断上一个数字是否满足条件呢?而不是当数组中有了新的数据就直接判断呢?
如果是后一种思想求解将造成很多的问题,且非常不方便,之前试过没有成功 . . .

~

测试代码就是读取一个数独,然后调用我们的 DFS方法(从 0开始):

/*
  世界上最难的数独:
    0 0 5 3 0 0 0 0 0
    8 0 0 0 0 0 0 2 0
    0 7 0 0 1 0 5 0 0
    4 0 0 0 0 5 3 0 0
    0 1 0 0 7 0 0 0 6
    0 0 3 2 0 0 0 8 0
    0 6 0 5 0 0 0 0 9
    0 0 4 0 0 0 0 3 0
    0 0 0 0 0 9 7 0 0
 */
cout << "请输入需要求解的数独:" << endl << endl;
for (size_t i = 0; i < 9; i++) 
    for (size_t j = 0; j < 9; j++)
   	cin >> arr[i][j];
   	
Dfs(0);   // 从第一个开始测试

这个世界上最难的解为:
在这里插入图片描述

我们可以用如下的方式来测试一下这个 DFS 用了多少 ms :
在这里插入图片描述
通过大量的测试后,我们发现要想求出这个解就要花费 80ms 左右的时间 . . .


浪子花梦

一个有趣的程序员 ~

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值