基礎演算法 深度優先搜尋

2022-06-20 01:57:09 字數 4073 閱讀 6053

祝食用愉快xd

(是一道胡亂出的題)

u56815 來走迷宮鴨!

深度優先搜尋,如果能不碰牆地到達右下角的出口,就把旗子立起來表示找到了出口。

什麼?你沒聽過深度優先搜尋

沒事,且聽我道來。

那,深度優先搜尋又是什麼呢?

拿走迷宮這事兒說起。如果你玩過\(mc\),或者無論從哪個去掉了解走迷宮的時候用的「右手法則」,那麼你或許能更容易理解深度優先搜尋。

如果你不了解這個法則,沒有關係。還記得希臘神話裡面的國王公尺諾斯吧。

他十分惡毒,造了乙個錯綜複雜的宮殿,任何外人進去,都會迷失路徑,別想再走出來,因此被稱做迷宮。公尺諾斯還在迷宮深處,豢養了乙隻牛首人身的怪物公尺諾陶洛斯。他用暴力迫使雅典城的國王愛琴,每九年貢納七對童男童女,作為牛怪的食物。有一年,雅典城又該貢獻童男童女了,人們在悲痛的氣氛中,準備用一艘掛著黑帆的船,把七對童男童女運送到克里特島去,雅典少年王子特修斯出於義憤,主動要求把自己作為童男之一送去,以便殺死牛怪,為民除害。特修斯來到了克里特島,公尺諾斯的女兒阿里阿德涅愛上了這位勇敢的王子,她偷偷送給他一柄魔劍和乙個線團。特修斯按照她的叮囑,在被送進迷宮之後,立即暗中把線的一端拴在宮門口,隨走隨放,一直進入迷宮深處。經過一番拼死搏鬥,特修斯終於用魔劍刺死了牛怪。

這位勇敢的王子是怎麼走進迷宮的呢?原來,他是通過線來標記自己來時的路,並及時標記已經走過的路徑,避免了被迷宮繞暈、重複走相同道路的可能。

把這玩意擱程設這兒,就叫深度優先搜尋(deep first search,dfs)。不可思議吧?

(似乎有點偏題了)好,我們回去看放在開頭的那道題。

剛才說了,搜尋是一種遞迴的列舉。怎麼遞迴呢?這道題我們可以通過向函式裡面傳座標達到搜尋的目的。

先上**,一點點解釋。

#includeint flag,n,m;

int state[521][521],vis[521][521];

void dfs(int x,int y);

int main()

void dfs(int x,int y)

if(x<=0||x>m||y<=0||y>n||state[x][y])return;//(1)

if(vis[x][y])return;//(2)

vis[x][y]=1;//(3)

dfs(x+1,y);//(4)

dfs(x,y+1);

dfs(x-1,y);

dfs(x,y-1);

}

這裡用到兩個陣列,\(state\)陣列和\(vis\)陣列,其中\(state\)陣列即是輸入的地圖,而\(vis[i][j]\)陣列代表這個點被沒被訪問過。

\((0)\)找到出口了!很好我可以退出了(x

\((1)\)如果搜尋的時候搜到地圖外面去了或者這個點有個大石頭墩子那肯定不行啊(x

\((2)\)如果訪問過了那也肯定不行啊(

\((3)\)既然行那就插個旗子表示這個點訪問過了emm

\((4)\)上下左右胡亂搜一通

\(0.\)這是乙個遞迴的程式,所以一定有返回的時候,不能一直一層層的向下遞迴。(這句話你都看不懂的話還是去回去複習複習遞迴吧)

\(1.\)那麼什麼時候返回呢?如果這個地方滿足了搜尋的初衷(找到該找的點了),那自然要返回。如果這個地方不能搜下去,自然需要返回。如果這個地方被搜過,也就是說再搜一遍這個點在這個題裡是沒有意義的(注意:有特例),所以返回。

\(2.\)注意到,上面的三種情況分別對應程式中的\((0)(1)(2)\)。

\(3.\)好了其實上面三條說的都是返回的情況。那麼下面來說dfs函式裡面除了返回能幹啥吧。

\(4.\)

這個題裡面似乎並不能幹啥

\(5.\)

我就是來湊夠五條的

把這個題稍微改一下,把出口改成所有右邊界上的點,咋辦呢?請思考

那麼這裡與原題唯一的不同就是返回的第一種情況。

給出dfs函式的參考**

void dfs(int x,int y)

if(x<=0||x>m||y<=0||y>n||state[x][y])return;

if(vis[x][y])return;

vis[x][y]=1;

dfs(x+1,y);

dfs(x,y+1);

dfs(x-1,y);

dfs(x,y-1);

}

喂喂你這不是沒改什麼東西麼

好啦,不開玩笑了,下面來介紹dfs函式內部能幹什麼。這題和剛才的差不多,只是保證肯定有解,要計算路的個數。這時候咋辦啊?

看到題別慌。先看啥時候退出遞迴

根據剛剛的五大定律,首先,如果找到出口了,那麼答案++,返回;其次,如果搜到地圖外面了或者不能走了或者訪問過了,那麼返回;再次,如果這條路上已經走過了這個點,(不能越搜越倒怵啊),那麼我們也要返回。

注意這裡的描述。

這次返回不是只要搜過就返回,而是在正在搜尋的這條路上搜過才返回。這如何實現呢?

略微思考一下就可以明白,這其實只是加入了乙個可以撤銷已訪問標籤的操作而已。也就是說,每次在換不同路徑的時候,都要把剛剛那條路徑當做沒走過一樣,因為必然不會再走一遍那一條路,但是新的答案可能會經過那乙個節點。這塊很繞,可能需要思考幾分鐘。

貼**:

#includeint ans,n,m,t;

int state[521][521],vis[521][521];

void dfs(int,int,int,int);

int main()

vis[x][y]=1;

dfs(x+1,y,tx,ty);

dfs(x,y+1,tx,ty);

dfs(x-1,y,tx,ty);

dfs(x,y-1,tx,ty);

vis[x][y]=0;//去除標記

}

這份**有乙個值得思考的地方:\((1)\)。

為什麼剛才的**可以不把這兩個判斷提前,而這個程式必須把這兩句話提前?

給一組資料希望能夠有助理解:

3 3 2

1 1 3 3

2 23 3

正解輸出:

錯誤輸出:

bhoj t110 亞頓的幻方

這其實已經有點偏離dfs了,只是純粹的遞迴。但無所謂。

#includeint i,j,n,a[1001][1001];

void search(int x,int y,int num)

void forget(int x,int y)

int judge(int x,int y)

void search(int begin,int n){//begin:搜到第幾列了

int i,j;

for(i=1;i<=n;i++){

if(judge(begin,i)){//如果這裡能放皇后

ans[begin]=i;

put(begin,i);//相當於剛剛那個題的vis[x][y]=1

if(begin誒誒誒!剛剛說好的必須有返回呢!!

emmm這難道是打臉

不是啦!\(return\)的意義其實在於防止遞迴一直一直一層一層在進行,導致爆棧。而這個函式\(for\)裡面的\(if\)語句有不能執行進去進行下一層遞迴的時候,這時候其實已經相當於\(return\)啦!

基礎演算法 深度優先搜尋DFS與廣度優先搜尋BFS

深搜 depth first search 和廣搜 breadth first search 是兩種基本搜尋演算法,均採用窮舉策略 下面以老鼠走迷宮 maze.cpp 為例給出它們的模板 問題描述 乙隻老鼠從迷宮的左上角走到右下角 如下圖 中間不能穿越障礙 陰影部分 任務 給出迷宮的形狀,請你求出老...

演算法基礎 城堡問題 深度優先搜尋演算法

題目 圖1是乙個城堡的地形圖。請你編寫乙個程式,計算城堡一共有多少房間,最大的房間有多大。城堡被分割成m n m 50,n 50 個方塊,每個方塊可以有0 4面牆。輸入 程式從標準輸入裝置讀入資料。第一行是兩個整數,分別是南北向 東西向的方塊數。在接下來的輸入行裡,每個方塊用乙個數字 0 p 50 ...

深度優先搜尋演算法

include include define vertexnum 9 struct node typedef struct node graph struct node head vertexnum 定義圖形結構 int visited vertexnum 頂點陣列 深度優先搜尋 void dfs ...