用編程的方式解決畢馬威圖形數獨
我爲什麼要寫這篇博客
博主的女朋友(你們別問我爲什麼會有女朋友)之前去一家公司面試,做到了這個題目。很多公司就把這個邏輯作爲一個面試的門檻,這個圖形數獨做不出來連面試的機會都沒有。悄悄的說一下,我女朋友是搞財務的(這TM跟邏輯有啥關係啊,頂多就是搞搞加減乘除)。不扯那麼多,我們往下看
什麼是圖形數獨,請看圖形分解
就是有四種圖形,問號處填什麼可以使每行每列的四個圖形都不一樣。
分析
這個不就是一個數獨嗎?只不過是一個四階的數獨,我們可以把圖形看成是1234這四個數字,空的地方用-1表示,我們用數獨求解的方式去解決不就行了嗎。
數獨怎麼解
我們可以用遞歸的方式解決
我們在空閒(-1)的地方依次填寫1,2,3,4,如果填寫1滿足條件,我們就在下個空閒的地方,繼續填寫,每放入一個數字,我們就要判斷衝突,如果衝突,就要在當前空閒的地方填入下個數字,如果四個數字都填寫過,我們回退一個空閒區,填入下個數字。以此類推,直到所有空閒都填滿爲止。
看不懂沒關係,上代碼,下面是C#的代碼,你們也可以改成Java的
public class Calc
{
private int n;//是n*n的
private int[,] mp;//二維數組-1表示空的 1~n 表示填入的數據
private int[,] result; //最終結果
public int[,] getMap()
{
return mp;
}
/// <summary>
/// 構造方法傳入參數
/// </summary>
/// <param name="mp"></param>
/// <param name="n"></param>
public Calc(int[,] mp, int n)
{
this.mp = mp;
this.n = n;
this.result = mp;
}
/// <summary>
/// 判斷某行某列能否放num這個數
/// </summary>
/// <param name="row"></param>
/// <param name="col"></param>
/// <param name="num"></param>
/// <returns></returns>
private bool solve(int row, int col, int num)
{
//用於記錄row col這個點所在的行列所有出現的數字
List<int> list = new List<int>();
for (int i = 0; i < 4; ++i)
{
if (mp[row, i] != -1)
{
//不存在就放入
if (!list.Contains(mp[row, i]))
{
list.Add(mp[row, i]);
}
}
if (mp[i, col] != -1)
{
//不存在就放入
if (!list.Contains(mp[i, col]))
{
list.Add(mp[i, col]);
}
}
}
//如果這個數已經出現過,就不能放入
if (list.Contains(num))
{
return false;
}
//沒有出現過,就可以放num這個數
return true;
}
/// <summary>
/// 深度優先遞歸放入數據
/// </summary>
/// <param name="cal"></param>
/// <param name="pel"></param>
public void dfs(int row, int col)
{
//遞歸結束條件,每次列加一,如果列越界,行加一,列清零
//如果行越界了,就表示所有的數都填完了,結束遍歷,將結果map賦值給result
if (row == n && col == 0)
{
//將結果map賦值給result
setResult();
return;
}
//下一個點的行列
int nextRow = row;
int nextCol = col;
//判斷列是否爲最後一個,如果是就變成下一行的第一個
if (col == (n - 1))
{
nextRow++;
nextCol = 0;
}
//否則列+1
else
{
nextCol++;
}
//如果當前點已經填了值,填充下一個點
if (mp[row, col] != -1)
{
dfs(nextRow, nextCol);
return;
}
//如果當前點沒有值,就依次判斷是否可以填入1~n
for (int i = 1; i <= n; i++)
{
//判斷row col這個點能否放入i這個數
if (solve(row, col, i))
{
mp[row, col] = i;
//能放入就給下個點放入數據
dfs(nextRow, nextCol);
}
}
//如果1~n都不能填,就置爲-1,讓上一個點繼續填入下個數
mp[row, col] = -1;
}
/// <summary>
/// 將結果map賦值給result
/// </summary>
private void setResult()
{
result = new int[n, n];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
result[i, j] = mp[i, j];
}
}
}
/// <summary>
/// 返回最終結果
/// </summary>
/// <returns></returns>
public int[,] getResult()
{
dfs(0, 0);
return result;
}
}
方法也有了,接下來不就可以用個界面包裝一下了嗎
界面包裝
給中間填充區域的每個Label添加點擊事件,記錄所選的行列(記錄在label22中)
然後給下面的選擇項添加點擊事件,點擊完後,如果label22中有值,就在那個行列中填充所選的圖案
點擊計算後,將圖案轉成數字,調用Calc的方案,返回結果,最後將結果顯示出來
int n = 4;
//獲得所有需要填充的數據
Dictionary<string, Label> dict = new Dictionary<string, Label>();
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (array[i, j].Text == "空")
{
dict.Add(i + "," + j, array[i, j]);
}
}
}
int[,] mp = new int[n, n];
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
string str = array[i, j].Text;
if (str == "空")
mp[i, j] = -1;
else if (str == "▲")
{
mp[i, j] = 1;
}
else if (str == "■")
{
mp[i, j] = 2;
}
else if (str == "✚")
{
mp[i, j] = 3;
}
else if (str == "●")
{
mp[i, j] = 4;
}
else
{
mp[i, j] = 5;
}
}
}
Calc calc = new Calc(mp, n);
mp = calc.getResult();
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
int num = mp[i, j];
if (num == -1)
{
array[i, j].Text = "空";
}
else if (num == 1)
{
array[i, j].Text = "▲";
}
else if (num == 2)
{
array[i, j].Text = "■";
}
else if (num == 3)
{
array[i, j].Text = "✚";
}
else if (num == 4)
{
array[i, j].Text = "●";
}
else
{
array[i, j].Text = "★";
}
if (dict.Keys.Contains(i + "," + j))
{
array[i, j].ForeColor = Color.Red;
}
}
}
以上就是整個代碼的解決方法
爲了操作的方便性,我添加了鍵盤快捷方式
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (label22.Text.Length == 0)
{
return;
}
string[] tagArray = label22.Text.Split(',');
int x = Convert.ToInt32(tagArray[0]);
int y = Convert.ToInt32(tagArray[1]);
if (e.KeyCode == Keys.NumPad0 || e.KeyCode == Keys.K || e.KeyCode == Keys.N)
{
array[x, y].Text = "空";
}
else if (e.KeyCode == Keys.NumPad1 || e.KeyCode == Keys.Y)
{
array[x, y].Text = "●";
}
else if (e.KeyCode == Keys.NumPad2 || e.KeyCode == Keys.J)
{
array[x, y].Text = "✚";
}
else if (e.KeyCode == Keys.NumPad3 || e.KeyCode == Keys.S)
{
array[x, y].Text = "▲";
}
else if (e.KeyCode == Keys.NumPad4 || e.KeyCode == Keys.Z || e.KeyCode == Keys.F)
{
array[x, y].Text = "■";
}
else if (e.KeyCode == Keys.NumPad5 || e.KeyCode == Keys.W)
{
array[x, y].Text = "★";
}
}
最終附上所有的完整的代碼
有了這工具,我女朋友還會怕那些公司讓我做這些題嗎?