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 左右的時間 . . .


浪子花夢

一個有趣的程序員 ~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章