毕马威图形数独
用编程的方式解决毕马威图形数独
我为什么要写这篇博客
博主的女朋友(你们别问我为什么会有女朋友)之前去一家公司面试,做到了这个题目。很多公司就把这个逻辑作为一个面试的门槛,这个图形数独做不出来连面试的机会都没有。悄悄的说一下,我女朋友是搞财务的(这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 = "★";
}
}
最终附上所有的完整的代码
有了这工具,我女朋友还会怕那些公司让我做这些题吗?