藍橋杯 DFS經典題 —— 算式900、寒假作業(告別枚舉法)

類似於 算式 900、寒假作業這種題目,可以直接暴力破解,但是它非常的浪費時間,而且程序不是太過於美觀,很容易讓我們對算法失去興趣,今天,我們告別傳統的暴力破解,使用偉大搜索算法 —— DFS(深度優先搜索) . . .
.
DFS 相關文章如下所示:
《算法筆記》—— “迷宮求解” 之 深度優先搜索(DFS)
《算法筆記》—— 圖 “鄰接矩陣” 的遍歷(DFS、BFS)
在這裏插入圖片描述


算式900

題目: 小明的作業本上有道思考題:

看下面的算式:

(□□□□-□□□□)*□□=900

其中的小方塊代表 0~9 的數字,這10個方塊剛好包含了 0 ~ 9 中的所有數字。
注意:0不能作爲某個數字的首位

小明經過幾天的努力,終於做出了答案!如下:
(5012-4987)*36=900

用計算機搜索後,發現還有另外一個解,本題的任務就是:請你算出這另外的一個解。

注意:提交的格式需要與示例嚴格一致;
括號及運算符號不要用中文輸入法;
整個算式中不能包含空格。
.

很多人看到這種類型的題目,第一想法就是暴力破解,實則沒必要,但是暴力破解快樂啊! 哈哈哈,看看下面的這個代碼,你還快樂嗎?
代碼太長我直接貼圖了:
在這裏插入圖片描述
看到這個代碼你真的很快樂嗎? 哈哈哈 ~ 其實我非常的痛苦,感覺腦子快要裂開了 (>_<) . . .

下面我們用 DFS 來解決一下這個題目,代碼如下:

#include <iostream>
using namespace std;

size_t flag[10];  // 標記 10 個數字是否被使用過
size_t arr[10];	  // 存儲 10 個滿足題目意思的書

void Dfs(int n)	  // 深搜思想
{
    if (n == 10)  // 已經找到 10 個不重複的數	
    {
     	// 判斷是否滿足題目的意思  == 900
  	if (((arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3]) -
   		(arr[4] * 1000 + arr[5] * 100 + arr[6] * 10 + arr[7])) *
   		(arr[8] * 10 + arr[9]) == 900)
  	{
   	    cout << " ( " << (arr[0] * 1000 + arr[1] * 100 + arr[2] * 10 + arr[3]) << " - " <<
    		(arr[4] * 1000 + arr[5] * 100 + arr[6] * 10 + arr[7]) << " ) " << " * " <<
    		(arr[8] * 10 + arr[9]) << " = " << 900 << endl;
  	}
	
	return;		// 不要忘記了這個返回
    }
    
    for (size_t i = 0; i < 10; ++i)	// 每一個數都有 10 種可能
    {
        if (i == 0 && (n == 0 || n == 4 || n == 8))	// 個別的數只有 9 種可能  開頭沒有 0 的情況
   	    continue;
  	
  	if (flag[i] == 0)		// 這個數字當前沒有被使用過
  	{
   	    flag[i] = 1;		// 標記爲使用過
   	    arr[n] = i;			// 將這個數存入數組之中
   	    Dfs(n + 1);			// 下一個位置的深搜
   	    flag[i] = 0;		// 這個數沒有用,取消標記(便於下個位置使用)
      	} 
    }
}

int main()
{
    Dfs(0);
    
    return 0;
}

使用 DFS 是不是非常的方便呢,結果如下所示:
在這裏插入圖片描述


.

寒假作業

題目:
現在小學的數學題目也不是那麼好玩的。
看看這個寒假作業:

□ + □ = □
□ - □ = □
□ × □ = □
□ ÷ □ = □

每個方塊代表 1 ~ 13 中的某一個數字,但不能重複。

思想過程: 這個題目我們繼續使用 DFS來求解,但我們在 DFS的過程中,會對程序進行優化,即數字個數每夠一個式子成立時,判斷這個式子是否滿足題意,比如3 個數字時 判斷 + 的那個式子,6 個 數字時判斷 - 的那個式子,9 個數字時判斷 * 的那個式子,12 個數字時判斷 / 的那個式子 . . .

優化過程,參考度娘上的大神 ^ _ ^

.

代碼如下所示:

#include <iostream>
using namespace std;

int arr[4][3];
int flag[14];
int sum;

// 判斷是否滿足式子的要求,優化過程
int JudgeIsNoGoOn(int n)
{
    int x = n / 3;
    
    if (n % 12 == 2)   // n == 2 時,判斷第一個式子是否滿足條件,下面同理 
    {
  	if (arr[x][0] + arr[x][1] != arr[x][2]) return 0;
    }
    
    else if (n % 12 == 5)
    {
  	if (arr[x][0] - arr[x][1] != arr[x][2]) return 0;
    }
    
    else if (n % 12 == 8)
    {
  	if (arr[x][0] * arr[x][1] != arr[x][2]) return 0;
    }
 
    else if (n % 12 == 11)
    {
  	if (arr[x][2] * arr[x][1] != arr[x][0]) return 0;
  	
  	printf("%d + %d = %d\n", arr[0][0], arr[0][1], arr[0][2]);
  	printf("%d - %d = %d\n", arr[1][0], arr[1][1], arr[1][2]);
  	printf("%d * %d = %d\n", arr[2][0], arr[2][1], arr[2][2]);
  	printf("%d / %d = %d\n\n\n", arr[3][0], arr[3][1], arr[3][2]);
    }
    
    return 1;
}

// 與上面差不多的思想,加個判斷而已 . . . 
void dfs(int n)
{
    int x = n / 3, y = n % 3;  // 需要存儲的二維數組的索引 
    
    // 到最後一個數字了,判斷是否滿足題目要求
    if (x == 4) { if (JudgeIsNoGoOn(11) == 1) ++sum; }
    else
    {
  	for (int i = 1; i <= 13; ++i)  // 每一個空位 都有 13 種可能性 
  	{
   	    if (flag[i] == 0)
   	    {
    		flag[i] = 1;
    		arr[x][y] = i;
    		
    		// 每存放一位數字,就判斷一次(可以前幾個式子就不滿足)
    		// 當前的數字不滿足條件,標記數組值還原,進行下一次的循環    
    		if (JudgeIsNoGoOn(n) == 0) 
    		{
     		    flag[i] = 0;
     		    continue;
    		}
    		
    		dfs(n + 1);     // 下一位數字繼續 
    		flag[i] = 0;
   	    }
  	} 
    }
}

熟悉的使用 DFS 或者 BFS,可以幫我們解決很多的問題,而且也有很大的成就感,哈哈 ~


.

浪子花夢

一個有趣的程序員 ~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章