數獨顧名思義——每個數字只能出現一次。數獨是一種源自18世紀末的瑞士,后在美國發展、並在日本得以發揚光大的數字謎題。數獨盤面是個九宮,每一宮又分為九個小格。在這八十一格中給出一定的已知數字和解題條件,利用邏輯和推理,在其他的空格上填入1-9的數字。使1-9每個數字在每一行、每一列和每一宮中都只出現一次。 這種游戲全面考驗做題者觀察能力和推理能力,雖然玩法簡單,但數字排列方式卻千變萬化,所以不少教育者認為數獨是訓練頭腦的絕佳方式。
九宮格的游戲在很多地方都經常出現,比如手機,電腦等等,下面就以簡單的一個例子來說明如何使用回溯算法來解決該類問題。下面數組代表最初給出的九宮格,
0為空格需要填的數字。
int[,] pu = new int[9, 9] { {0,0,0,7,2,8,0,0,0}, {0,9,0,0,5,1,6,0,0}, {0,0,0,0,6,0,0,8,2}, {3,0,0,8,0,2,7,0,4}, {1,7,4,0,3,0,0,2,0}, {2,8,0,5,0,0,0,3,0}, {0,1,0,3,0,0,2,0,0}, {0,0,7,0,4,6,0,0,5}, {0,0,6,1,0,0,0,4,9} };
下面使用回溯算法來解決該類問題:
1.驗證函數如下:
private bool IsValid(int i, int j) { int n = pu[i,j]; int[] query=new int[9]{0,0,0,3,3,3,6,6,6}; int t, u; for (t = 0; t < 9; t++) { if ((t != i && pu[t, j] == n) || (t != j && pu[i, t] == n)) return false; } for (t = query[i]; t < query[i] + 3; t++) { for (u = query[j]; u < query[j] + 3; u++) { if ((t != i || u != j) && pu[t, u] == n) return false; } } return true; }
2.顯示函數:
private void Show()
{
int n=0; for (var i = 0; i < 9; i++) { for (var j = 0; j < 9; j++) { Console.Write(pu[i, j] + " "); } Console.WriteLine(); } Console.WriteLine("----------------------------------------------"); }
3.使用回溯算法求解:
private void Try(int n) { if (n == 81) {//是否已經是最后一個格子 Show(); return; } int i = n / 9, j = n % 9; if (pu[i,j] != 0) {//如果當前格子不需要填數字,就跳到下一個格子 Try(n + 1); return; } for (int k = 0; k < 9; k++) { pu[i,j]++;//當前格子進行嘗試所有解 if (IsValid(i, j)) Try(n + 1);//驗證通過,就繼續下一個 } pu[i,j] = 0; //如果上面的單元無解,就回溯 }
4.調用如下:
public void Test()
{
Show();
Try(0);
}
實際在游戲中會有不同的游戲級別如:
低級:要求至少有一行或一列出題時已填上5個數據,其他可以隨機安排,但是每行或每列必須有數據
中級:要求至少有一行或一列出題時已填上4個數據,其他可以隨機安排,但是每行或每列必須有數據
高級:要求至少有一行或一列出題時已填上3個數據,其他可以隨機安排,但是每行或每列必須有數據
所以實際中我們使用回溯不是很方便,我說下我的方法:
1.只要一個九宮格的任意兩行,兩列所在列有相同的數字,那么這兩行,兩列交叉后該九宮格就只剩下一格,這個格子就是前面的相同數字。
2.使用1的思想,如果一個九宮格任意兩行一列,或兩列一行所在行列都有相同數字,那么該九宮兩行一列交叉后只剩兩個格子,如果一個格子游戲已經給出
數字,剩下的格子就是該數字了。
3.一次類推,一行一列,0行1列等等。
作者:
生魚片
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。