畢馬威圖形數獨

我爲什麼要寫這篇博客

博主的女朋友(你們別問我爲什麼會有女朋友)之前去一家公司面試,做到了這個題目。很多公司就把這個邏輯作爲一個面試的門檻,這個圖形數獨做不出來連面試的機會都沒有。悄悄的說一下,我女朋友是搞財務的(這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地址

有了這工具,我女朋友還會怕那些公司讓我做這些題嗎?

發佈了8 篇原創文章 · 獲贊 33 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章