關於DFS的一些拙劣的理解

2021-09-29 11:25:46 字數 3206 閱讀 5882

其實我真的還不是很懂dfs和bfs,雖然感覺原理上理解了,可用它做題的時候總會被一些奇奇怪怪的東西卡住。。。。。。

於是做了乙個大膽的決定,我來試試一邊整理一邊學習一邊寫部落格來學習這兩個個神奇的東東。。。。。。qwq

所以這兩篇部落格可能會寫很久很久。。。。。。也很可能沒有邏輯沒有條理。。。。。。嗯,反正只有我自己看,看的懂就好,應該沒事吧。。。。。。qwq

現在先開始寫有關dfs的一些理解

dfs的意思是深度優先搜尋,它是一種遍歷或搜尋樹或圖的演算法。總感覺只用語言來描述這個演算法的思想太過繁雜難懂了,所以我在這裡用圖的方式,主要目的是理解dfs的用法,可能有些在定義上不準確的地方,不過個人感覺思想和用法才是最關鍵的部分。

所以讓我們來先看看圖:

這是乙個簡單的樹狀圖,我們現在利用這個圖來出一道題目:

現在我們從v1出發,走到v10,那麼一共有多少條合法路徑可以到達v10?

現在我們開始用dfs的思想開始對這個過程進行模擬。

我們先讓節點由v1 -> v2,然後v2 -> v4

就像這樣,綠色代表我們已經搜尋過的路徑。當我們到v4時,我們發現已經沒有更深的路了,而且v4 != v10,所以這條路是死路,我們就返回到v2尋找,就會發現還有條路,v2 -> v5,v5 -> v8。

我們把已經「廢掉」的路標記為黑色,開始搜尋新發現的路徑,直到v8發現路又沒了,而且v8 != v10,於是我們返回到v5,把v8標記為黑色,再找新的路。

這時發現,我們從v5 -> v9 -> v10這條路是可行的,於是我們找到了一條真確的路,我們把真確的路標記成黃色,然後讓可行路總數加一,然後再返回v1,對v1連線的另乙個點進行同樣的道路搜查,最後會發現三條可行的路。

這就是最簡單的dfs問題,對一條一條路進行遍歷,可行就返回真值,不可行就返回上一節點搜查,直至把整個樹或圖搜尋完。

對演算法的理解,一般是要題目輔助的,接下我會給出一些例題,來更深一步的理解dfs的用法。

乙個8 * 8的棋盤,我們現在要在上面擺上8個皇后,要求它們不會把對方吃掉(即不在同一行,同一列,也不在45度對角線上),那麼我們一共有多少種方法來擺放棋子呢?

這是乙個很經典的dfs問題,對於這一問題,我覺得主要的難點是在理解dfs後,你應該怎麼把這個問題轉換成乙個樹或者圖(這也是我一直做的不好的地方),轉換成功後,你應該就可以很輕鬆的解決這個問題了。

#include

using

namespace std;

int sum =0;

const

int n =20;

int a[n]

;bool

found

(int i,

int k)

//用於判斷在此行放置皇后的當時位置

return1;

}void

dfs(

int k)}}

}int

main

(void

)

簡單理解它的思想,把棋盤轉換成有8個「頭」的樹,每個「頭」又連線著8個節點,然後每個節點又連線著8個子節點。。。。。。

用這樣的方法,生成乙個巨大的樹,在滿足條件的前提下,只要到樹的最根部,最算是完成了一次合法路徑的尋找。

用這樣的演算法來解決8皇后問題,它所用的標記方法是直接利用圖來標記,(感覺自己一直不太對這玩意感冒,雖然知道是那麼個東西,但總是換道題就廢了。。。)其實,也可以利用vis[ ]陣列來進行標記,只要理解了核心演算法後,另一種標記方式的**也就可以輕鬆打出了,在這就不例舉了,我們來直接看下一道例題,我會用vis[ ]標記的方式來完成這道題。

在乙個給定形狀的棋盤(形狀可能是不規則的)上面擺放棋子,棋子沒有區別。要求擺放時任意的兩個棋子不能放在棋盤中的同一行或者同一列,請程式設計求解對於給定形狀和大小的棋盤,擺放k個棋子的所有可行的擺放方案c。

輸入:有多組輸入,每組資料的第一行是兩個正整數,n k,用乙個空格隔開,表示了將在乙個n*n的矩陣內描述棋盤,以及擺放棋子的數目。 n <= 8 , k <= n

當為-1 -1時表示輸入結束。

隨後的n行描述了棋盤的形狀:每行有n個字元,其中 # 表示棋盤區域, . 表示空白區域(資料保證不出現多餘的空白行或者空白列)。

輸出:對於每一組資料,給出一行輸出,輸出擺放的方案數目c (資料保證c<2^31)。

這也是一道很經典的用dfs演算法完成的題目,與dfs的核心思想十分貼近

#include

#include

#include

using

namespace std;

int sum, vis[20]

[20];

char a[20]

[20];

int n, m;

void

dfs(

int k,

int dis)

//k代表行數,dis代表棋子數}}

}}intmain()

} sum =0;

//初始化sum為0

memset

(vis,0,

sizeof

(vis));

//初始化vis[ ]為0

dfs(

0, m)

; cout << sum << endl;

}return0;

}

其實這一題的思想和上一題基本相同,也是吧棋盤轉化為樹,然後利用dfs的思想對數進行遍歷,就可以得出答案。

最後一點,應為dfs的遍歷是一條枝幹一條枝幹進行的,其時間複雜度最壞的情為o(!n),所以,用上面的辦法是無法對太大的樹進行索查的,在難的題目中,我們往往會用的各種神奇的剪枝的操作。(雖然我沒遇到過,我也不會。。。)所以,上面寫的題目和思想,真的是dfs用法中的基礎,革命的道路還在十分的漫長。。。。。。

關於熵的一些理解

對於理工科學生來說,熵 並不是乙個陌生的名詞。在諸如 大學物理 熱力學 和 資訊理論 等課程中都會有所介紹。但同時 熵 又是乙個顯得有點神秘的概念,看不見也摸不著。我最早是在高中物理課中聽說的,大概是在介紹 熱力學第二定律 時提到的。熱力學第二定律的內容是 熱力學過程是不可逆的 孤立系統自發地朝著熱...

關於float的一些理解

float是否脫離文件流,乙個父元素不設定overflow的話,子元素float,就不會把父元素撐開,換句話說,他就不會有高度,但是做個demo 父元素overflow hidden 子元素前兩個float,第三個不float,結果是第三個沒有clear浮動的元素,跟float的元素出現在同乙個位置...

關於android layout的一些理解

1 wrap content view的尺寸根據它的內容確定 match parent view的尺寸盡量和它的parent view group一樣大 2 獲得view的位置 position getleft gettop getright getleft getwidth getwidth 3 ...