--- title: CP進階 05.basic graph theory tags: 1th,CP進階 --- # CP進階 05.basic graph theory [ppt](https://drive.google.com/file/d/1-qFo8Mafrr2c2cGId_tOcjOa2eVI-BG_/view?usp=sharing) [ppt(revised)](https://drive.google.com/file/d/1OT1Pl9i4HYXiUy2FtM2Qst0U6xZn-Bo9/view?usp=sharing) [ppt2](https://drive.google.com/file/d/1OlmbjgA_2uLgzo6Y2alMJ1M4d7Lyh6mG/view?usp=sharing) [題單](https://codeforces.com/contestInvitation/20e2004cbd2e1dcf435dd54ecd01f618168d2422) author: __Shioko ## flood fill algorithm 所謂的flood fill algorithm,指的就是像淹水一般,讓水從某一個點擴散開來。可以把它想成專門在方格圖上用的BFS(? (一個flood fill的例子(圖由左到右)) ![一個flood fill的例子(由左到右)](https://i.imgur.com/saQStSR.png) #### 概念 基本上flood fill algorithm的核心想法和BFS差不多,都是依照距離原點的距離由近到遠依序尋訪,詳細的部分在課堂上已經講過,就不再贅述。 #### 實作 :「既然跟BFS差不多,代表說我可以直接套BFS,再稍微改一下就好了對吧?」 是的,基本上flood fill algorithm長得跟BFS很像,但是第一次寫的人常常會寫成這樣。 ```cpp= const int N = 100; bool graph[N][N]; //false -> no obstacle, true -> have obstacle bool visited[N][N]; void floodFill(pair<int, int> S) { queue<pair<int, int> > q; visited[S.first][S.second] = true; q.push(S); while(!q.empty()) { pair<int, int> now = q.front(); q.pop(); if (!visited[now.first + 1][now.second] and !graph[now.first + 1][now.second]) { visited[now.first + 1][now.second] = true; q.push(make_pair(now.first + 1, now.second)); } if (!visited[now.first - 1][now.second] and !graph[now.first - 1][now.second]) { visited[now.first - 1][now.second] = true; q.push(make_pair(now.first - 1, now.second)); } if (!visited[now.first][now.second + 1] and !graph[now.first][now.second + 1]) { visited[now.first][now.second + 1] = true; q.push(make_pair(now.first, now.second + 1)); } if (!visited[now.first][now.second - 1] and !graph[now.first][now.second - 1]) { visited[now.first][now.second - 1] = true; q.push(make_pair(now.first, now.second - 1)); } } } ``` 光是4方位的擴散就要4個if,那如果今天要寫的是8方位擴散,或著是西洋棋裡的騎士步怎麼辦?難道要寫8個if嗎? 稍微觀察一下這份code,可以發現每個if裡面除了index稍微不一樣以外,在做的事是相同的,因此我們可以用一點巧思,把所有擴散的方位用陣列存起來,讓它變成一個for迴圈就能搞定的事。 就像這樣: ```cpp= const int N = 100; bool graph[N][N]; //false -> no obstacle, true -> have obstacle bool visited[N][N]; const int dx[4] = {1, -1, 0, 0}; const int dy[4] = {0, 0, 1, -1}; void floodFill(pair<int, int> S) { queue<pair<int, int> > q; visited[S.first][S.second] = true; q.push(S); while(!q.empty()) { pair<int, int> now = q.front(); q.pop(); for(int i = 0; i < 4; i++) { pair<int, int> nxt = make_pair(now.first + dx[i], now.second + dy[i]); if (!visited[nxt.first][nxt.second] and !graph[nxt.first][nxt.second]) { visited[nxt.first][nxt.second] = true; q.push(nxt); } } } } ``` 透過用陣列去儲存所有擴散的方向,就可以將一堆if壓成一個for迴圈能搞定的東西,並且能夠輕鬆地擴充功能,改成8方位擴散只要改一下陣列內容就好了! ```cpp= const int dx[8] = {1, 1, 1, 0, -1, -1, -1, 0}; const int dy[8] = {1, 0, -1, -1, -1, 0, 1, 1}; ``` 當然,如果你想要在方格圖上用一般的BFS也不是不行,只要在每個相鄰的方格之間建邊即可,只是這樣擴充性不好,耗費的空間也很大... #### 一個小技巧 在做flood fill的時候,有時候可能會尋訪到陣列的邊界外面,因此要寫很惱人的邊界判斷,不過其實只要在圖的外圍圍一圈障礙物就可以不用判斷邊界了喔~ ![](https://i.imgur.com/RllOGRR.png) ## 額外練習題 [ZJ c126.](https://zerojudge.tw/ShowProblem?problemid=c126) BST還原(經典題) [CSES Counting Rooms](https://cses.fi/problemset/task/1192) (flood fill模板題)