毕马威图形数独

 

 

 

我为什么要写这篇博客

博主的女朋友(你们别问我为什么会有女朋友)之前去一家公司面试,做到了这个题目。很多公司就把这个逻辑作为一个面试的门槛,这个图形数独做不出来连面试的机会都没有。悄悄的说一下,我女朋友是搞财务的(这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 = "★";
            }
        }

最终附上所有的完整的代码

Gitee地址

有了这工具,我女朋友还会怕那些公司让我做这些题吗?

posted @ 2019-12-31 18:24  大师兄法号随缘  阅读(1373)  评论(0编辑  收藏  举报