用寬度優先搜尋(BFS)解決分油問題

2021-08-29 20:48:37 字數 2070 閱讀 4066

選了李翠華老師的計算智慧型課程,第一次作業是解決乙個小孩分油問題,把實驗報告和**上傳,留底。

1.問題描述

編寫乙個程式,解決下列問題:

小孩分油問題:兩個小孩去打油,一人帶了乙個一斤的空瓶,另乙個帶了乙個七兩、乙個三兩的空瓶。原計畫各打一斤油,可是由於所帶的錢不夠,只好兩人合打了一斤油,在回家的路上,兩人想平分這一斤油,可是又沒有其它工具。試僅用三個瓶子(一斤、七兩、三兩)精確地分出兩個半斤油來。

2.演算法設計

資料結構:

vector<

int> b;

/*乙個vector中有三個數,分別表示一斤瓶、七兩瓶和三兩瓶的油量*/

bool vis[

10010];

/*記錄某狀態是否被遍歷過,乙個狀態用b[0]*100+b[1]*10+b[2]表示*/

int cap[3]

=;/*記錄每個瓶子的容量*/

queue

int>

> q;

/*bfs中使用的佇列*/

struct path

由題意分析這可以用搜尋做,一共有三個瓶子,每個瓶子可以向其他兩個瓶子倒油,或者保持自己當前的狀態,也就是每次輪到瓶子x,它有三條路可以走,所以這時用寬度優先搜尋(bfs),每次搜尋三個狀態,當目前狀態為(5,5,0)時退出,這時我們搜到的第一結果必定是最短路,也就是倒油次數最少的方法。這邊需要注意的是我們需要輸出倒油的順序,也就是需要記錄寬度優先搜尋的路徑。其a->b的狀態轉移過程可由下圖表示:

其中,a、b分別表示瓶子a、b中目前的油量,cap(b)表示瓶子b的容量,cap為capacity的縮寫。

由上圖1可知,雖然我們有三種瓶子,但只需要乙個函式transform(vector b, int f, int t)就能表示所有的轉移,f(from)表示倒出油的瓶子,t(to)表示倒入油的瓶子。

由於a->b後,b也可以向a轉移,為了避免出現死迴圈,這裡我使用了記憶化搜尋,對於每次倒油,維護乙個布林陣列vis,記錄這個狀態是否被遍歷過,若是遍歷過,下次經過時就直接跳過。當遇到狀態(5,5,0)時退出搜尋,並輸出路徑以及所用的步數。

3.程式流程

1、用初始化vector b;

2、呼叫bfs函式;

3、將b存入佇列q中,使用while迴圈判斷q是否為空,若為空則退出;

4、若不為空,則將q的第乙個節點取出,f++,f表示當前節點有多少個,記錄路徑用;

5、判斷當前節點是否為(5,5,0),若是則將記錄當前節點f,並回到main函式,否則轉到6;

6、使用for迴圈,分別對vector中的三個節點使用transform函式,若是transform函式出來的結果陣列是已經被遍歷過,則不處理,若沒遍歷過,就將這個狀態記錄到路徑中,並將該節點存入q中;

7、當回到main函式後,呼叫output函式來輸出路徑,由於我們找到的節點f是最後乙個節點,所以我們需要先回溯到第乙個節點,再輸出。遞迴output(path[f].pre)找到pre為0的點,然後再輸出就可以了。

4.核心偽**

vector<

int>

transform

(vector<

int> b,

int f,

int t)

else

return b;

}void

bfs(vector<

int> b)

q.pop();

for(

int i =

0;i <

3;i++

)else}}

}}return;}

void

output

(int i)

簡單測試:

寬度優先搜尋BFS

寬度優先搜尋 bfs,breadth first search 也是搜尋的手段之一。它與深度優先搜尋類似,從某個狀態出發探索所有可以到達的狀態。與深度優先搜尋的不同之處在於搜尋的順序,寬度優先搜尋總是先搜尋距離初始狀態近的狀態。也就是說,它是按照開始狀態 只需1次轉移就可以到達的所有狀態 只需2次轉...

寬度優先搜尋(BFS)

bfs,其英文全稱是breadth first search。0 1 4 2 3思路 從圖中某個節點出發,將該結點入隊,標記為已訪問。然後輸出佇列的隊首 第一次為起點 將隊首從佇列中刪除,訪問該結點的鄰接表,將未標記的相鄰結點入隊,並且標記為已訪問。接下來迴圈操作第二句話。按照上邊的圖,假設出發點為...

寬度優先搜尋bfs

好吧,今天看了bfs,其實發現基本思想也是不過如此。只是,應用還是不太會。bfs是寬度優先,從根節點開始,依次訪問它的所有鄰接點,然後再按順序訪問鄰接點的鄰接點,先被訪問的點的鄰接點先被訪問。由於要按這樣的順序訪問,所以需要用到佇列。求最短路徑和迷宮型別的題目都可以利用bfs.下面是基本步驟 1 從...