2.1.1 BFS中的Flood Fill和最短路模型
BFS特点
求最小->第一次搜到恰为最小距离
基于迭代,不会爆栈(一般1M,十万层)
类型最小距离->从内部一点到另一点的最小距离
eg.走迷宫
最小步数->整体视为一个状态,状态的变换的最小步数(状态本身作为一个点,每次变换视为一步,问题是状态之间的逻辑关系)
eg.八数码
一. Flood Fill算法
可以在线性时间复杂度内,找到某个点所在的连通块
使用BFS的过程模拟洪水覆盖的过程–从四周开始扩散–填充整个连通块
连通类型四连通八连通
注意双层逻辑:先遍历每个格子,每个格子再进行BFS搜索过程。需进行标记–判重。
BFS使用队列实现,队列中存储下标,二维时可以使用pair
注意是否开始搜索的判断条件要想清楚
搜索时注意遍历方法和边界条件
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 #include <cstring> #include <iostream> #include <algorithm> #define x first #define y second using namespace std;typedef pair<int , int > PII; const int N = 1010 , M = N * N;int n, m;char g[N][N]; PII q[M]; bool st[N][N]; void bfs (int sx, int sy) { int hh = 0 , tt = 0 ; q[0 ] = {sx, sy}; st[sx][sy] = true ; while (hh <= tt){ PII t = q[hh ++]; for (int i = t.x - 1 ; i <= t.x + 1 ;++ i) for (int j = t.y - 1 ; j <= t.y + 1 ; ++ j){ if (i == t.x && j == t.y) continue ; if (i < 0 || i >= n || j < 0 || j >= m) continue ; if (g[i][j] == '.' || st[i][j]) continue ; q[++ tt] = {i, j}; st[i][j] = true ; } } } int main () { scanf ("%d%d" , &n, &m); for (int i = 0 ; i < n; ++ i) scanf ("%s" , g[i]); int cnt = 0 ; for (int i = 0 ; i < n; ++ i) for (int j = 0 ; j < m; ++ j) if (g[i][j] == 'W' && !st[i][j]) { bfs (i, j); cnt ++; } printf ("%d\n" , cnt); return 0 ; }
python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 from collections import dequedef main (): n, m = map (int , input ().split()) g = [list (input ()) for _ in range (n)] d_x = (-1 , -1 , -1 , 0 , 0 , 1 , 1 , 1 ) d_y = (-1 , 0 , 1 , -1 , 1 , -1 , 0 , 1 ) cnt = 0 def bfs (sx, sy ): q = deque([(sx, sy)]) g[sx][sy] = '.' while q: x, y = q.popleft() for dx, dy in zip (d_x, d_y): nx, ny = x + dx, y + dy if 0 <= nx < n and 0 <= ny < m and g[nx][ny] == 'W' : q.append((nx, ny)) g[nx][ny] = '.' for i, line in enumerate (g): for j, ch in enumerate (line): if ch == 'W' : bfs(i, j) cnt += 1 print (cnt) if __name__ == "__main__" : main()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 def main (): n, m = map (int , input ().split()) g = [list (input ()) for _ in range (n)] d_x = (-1 , -1 , -1 , 0 , 0 , 1 , 1 , 1 ) d_y = (-1 , 0 , 1 , -1 , 1 , -1 , 0 , 1 ) q = [0 ] * (n * m) st = [[False ] * m for _ in range (n)] cnt = 0 def bfs (sx, sy ): hh, tt = 0 , 0 q[0 ] = (sx, sy) st[sx][sy] = True while hh <= tt: x, y = q[hh] hh += 1 for dx, dy in zip (d_x, d_y): nx, ny = x + dx, y + dy if 0 <= nx < n and 0 <= ny < m and g[nx][ny] == 'W' and not st[nx][ny]: tt += 1 q[tt] = (nx, ny) st[nx][ny] = True for i in range (n): for j in range (m): if g[i][j] == 'W' and not st[i][j]: bfs(i, j) cnt += 1 print (cnt) if __name__ == "__main__" : main()
四连通遍历
取二进制中第k位
x >> k & 1
本题注意二进制解码的方法
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 #include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #define x first #define y second using namespace std;typedef pair<int , int > PII;const int N = 55 , M = N * N;int n, m;int g[N][N];PII q[M]; bool st[N][N];int bfs (int sx, int sy) { int dx[4 ] = {0 , -1 , 0 , 1 }, dy[4 ] = {-1 , 0 , 1 , 0 }; int hh = 0 , tt = 0 ; int area = 0 ; q[0 ] = {sx, sy}; st[sx][sy] = true ; while (hh <= tt){ PII t = q[hh ++]; area ++; for (int i = 0 ; i < 4 ; ++ i){ int a = t.x + dx[i], b = t.y + dy[i]; if (a < 0 || a >= n || b < 0 || b >= m) continue ; if (st[a][b]) continue ; if (g[t.x][t.y] >> i & 1 ) continue ; q[++ tt] = {a, b}; st[a][b] = true ; } } return area; } int main () { cin >> n >> m; for (int i = 0 ; i < n; ++ i) for (int j = 0 ; j < m; ++ j) cin >> g[i][j]; int cnt = 0 , area = 0 ; for (int i = 0 ; i < n; ++ i) for (int j = 0 ; j < m; ++ j) if (!st[i][j]){ area = max (area, bfs (i, j)); cnt ++; } cout << cnt << endl; cout << area << endl; return 0 ; }
python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 from collections import dequedef main (): n, m = map (int , input ().split()) g = [list (map (int , input ().split())) for _ in range (n)] d_x = [0 , -1 , 0 , 1 ] d_y = [-1 , 0 , 1 , 0 ] st = [[False ] * m for _ in range (n)] def bfs (sx, sy ): q = deque([[sx, sy]]) st[sx][sy] = True area = 0 while q: x, y = q.popleft() area += 1 for i in range (4 ): nx, ny = x + d_x[i], y + d_y[i] if nx < 0 or nx >= n or ny < 0 or ny >= m: continue if st[nx][ny]: continue if g[x][y] >> i & 1 : continue q.append([nx, ny]) st[nx][ny] = True return area cnt, area = 0 , 0 for i in range (n): for j in range (m): if not st[i][j]: area = max (area, bfs(i, j)) cnt += 1 print (cnt) print (area) if __name__ == '__main__' : main()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 def main (): n, m = map (int , input ().split()) g = [list (map (int , input ().split())) for _ in range (n)] d_x = [0 , -1 , 0 , 1 ] d_y = [-1 , 0 , 1 , 0 ] st = [[False ] * m for _ in range (n)] q = [0 ] *(m*n) def bfs (sx, sy ): q[0 ] = [sx, sy] st[sx][sy] = True area = 0 hh, tt = 0 , 0 while hh <= tt: x, y = q[hh] hh += 1 area += 1 for i in range (4 ): nx, ny = x + d_x[i], y + d_y[i] if nx < 0 or nx >= n or ny < 0 or ny >= m: continue if st[nx][ny]: continue if g[x][y] >> i & 1 : continue tt += 1 q[tt] = [nx, ny] st[nx][ny] = True return area cnt, area = 0 , 0 for i in range (n): for j in range (m): if not st[i][j]: area = max (area, bfs(i, j)) cnt += 1 print (cnt) print (area) if __name__ == '__main__' : main()
本题要判断每个方格和周围方格的关系
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 #include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #define x first #define y second using namespace std;typedef pair<int , int > PII;const int N = 1010 , M = N * N;int n;int h[N][N];PII q[M]; bool st[N][N];void bfs (int sx, int sy, bool & has_higher, bool & has_lower) { int hh = 0 ,tt = 0 ; q[0 ] = {sx, sy}; st[sx][sy] = true ; while (hh <= tt){ PII t = q[hh ++]; for (int i = t.x - 1 ; i <= t.x + 1 ; ++ i) for (int j = t.y - 1 ; j <= t.y + 1 ; ++ j){ if (i < 0 || i >= n || j < 0 || j >= n) continue ; if (h[i][j] != h[t.x][t.y]){ if (h[i][j] > h[t.x][t.y]) has_higher = true ; else has_lower = true ; } else if (!st[i][j]){ q[++ tt] = {i, j}; st[i][j] = true ; } } } } int main () { scanf ("%d" , &n); for (int i = 0 ; i < n; ++ i) for (int j = 0 ; j < n; ++ j) scanf ("%d" , &h[i][j]); int peak = 0 , valley = 0 ; for (int i = 0 ; i < n; ++ i) for (int j = 0 ; j < n;++ j) if (!st[i][j]){ bool has_higher = false , has_lower = false ; bfs (i, j, has_higher, has_lower); if (!has_higher) peak ++; if (!has_lower) valley ++; } printf ("%d %d\n" , peak, valley); return 0 ; }
python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 from collections import dequedef main (): n = int (input ()) h = [list (map (int , input ().split())) for _ in range (n)] st = [[False ] * n for _ in range (n)] def bfs (sx, sy ): q = deque([[sx, sy]]) st[sx][sy] = True has_higher, has_lower = False , False while q: x, y = q.popleft() for i in range (x - 1 , x + 2 ): for j in range (y - 1 , y + 2 ): if i == x and j == y: continue if i < 0 or i >= n or j < 0 or j >= n: continue if h[i][j] > h[x][y]: has_higher = True elif h[i][j] < h[x][y]: has_lower = True elif not st[i][j]: q.append([i, j]) st[i][j] = True return has_higher, has_lower peak, valley = 0 , 0 for i in range (n): for j in range (n): if not st[i][j]: has_higher, has_lower = bfs(i, j) if not has_higher: peak += 1 if not has_lower: valley += 1 print (peak, valley) if __name__ == '__main__' : main()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 def main (): n = int (input ()) h = [list (map (int , input ().split())) for _ in range (n)] st = [[False ] * n for _ in range (n)] q = [0 ] * (n*n); def bfs (sx, sy ): q[0 ] = (sx, sy) st[sx][sy] = True ; hh, tt = 0 , 0 has_higher , has_lower = False , False while hh <= tt: x, y = q[hh] hh += 1 for i in range (x - 1 , x + 2 ): for j in range (y - 1 , y + 2 ): if i == x and j == y: continue if i < 0 or i >= n or j < 0 or j >= n: continue if h[i][j] > h[x][y]: has_higher = True elif h[i][j] < h[x][y]: has_lower = True elif not st[i][j]: tt += 1 q[tt] = (i, j) st[i][j] = True return has_higher, has_lower peak, vallery = 0 , 0 for i in range (n): for j in range (n): if not st[i][j]: has_higher, has_lower = bfs(i, j) if not has_higher: peak += 1 if not has_lower: vallery += 1 print (peak, vallery) if __name__ == '__main__' : main()
二. 最短路模型
宽搜具有最短路性质:
当所有边的权重相等时,使用宽搜可以在线性复杂度内得到起点到所有点的最短路(单源最短路)。
第一次搜到一定是最短的,不需要记录额外的距离信息。
特殊的dijkstra算法->权重相同时,按层搜索使队列为优先队列
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #define x first #define y second using namespace std;typedef pair<int , int > PII;const int N = 1010 , M = N * N;int n;int g[N][N];PII q[M]; PII pre[N][N]; void bfs (int sx, int sy) { int dx[] = {-1 , 0 , 1 , 0 }, dy[] = {0 , -1 , 0 , 1 }; int hh = 0 , tt = 0 ; q[0 ] = {sx, sy}; memset (pre, -1 , sizeof pre); pre[sx][sy] = {0 , 0 }; while (hh <= tt){ PII t = q[hh ++]; for (int i = 0 ; i < 4 ; ++ i){ int a = t.x + dx[i], b = t.y + dy[i]; if (a < 0 || a >= n || b < 0 || b >= n) continue ; if (g[a][b] == 1 ) continue ; if (pre[a][b].x != -1 ) continue ; q[++ tt] = {a, b}; pre[a][b] = t; } } } int main () { scanf ("%d" , &n); for (int i = 0 ; i < n; ++ i) for (int j = 0 ; j < n; ++ j) scanf ("%d" , &g[i][j]); bfs (n - 1 , n - 1 ); PII end (0 , 0 ) ; while (true ){ printf ("%d %d\n" , end.x, end.y); if (end.x == n - 1 && end.y == n - 1 ) break ; end = pre[end.x][end.y]; } return 0 ; }
python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 from collections import dequedef main (): n = int (input ()) g = [list (map (int , input ().split())) for _ in range (n)] pre = [[[-1 , -1 ] for _ in range (n)] for _ in range (n)] dx = [-1 , 0 , 1 , 0 ] dy = [0 , -1 , 0 , 1 ] def bfs (sx, sy ): q = deque([[sx, sy]]); pre[sx][sy] = [sx, sy] while q: x, y = q.popleft() for i in range (0 , 4 ): a = x + dx[i] b = y + dy[i] if a < 0 or a >= n or b < 0 or b >= n: continue if g[a][b] == 1 : continue g[a][b] = 1 q.append([a, b]) pre[a][b] = [x, y] bfs(n - 1 , n - 1 ) i, j = 0 , 0 while True : print (i, j) if i == n - 1 and j == n - 1 : break i, j = pre[i][j] if __name__ == '__main__' : main()
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #define x first #define y second using namespace std;typedef pair<int , int > PII;const int N = 155 , M = N * N;int n, m;char g[N][N];PII q[M]; int dist[N][N]; int bfs () { int dx[] = {-2 , -1 , 1 , 2 , -2 , -1 , 1 , 2 }; int dy[] = {-1 , -2 , -2 , -1 , 1 , 2 , 2 , 1 }; int hh = 0 , tt = 0 ; int sx, sy; for (int i = 0 ; i < n; ++ i) for (int j = 0 ; j < m; ++ j) if (g[i][j] == 'K' ) sx = i, sy = j; q[0 ] = {sx, sy}; memset (dist, -1 , sizeof dist); dist[sx][sy] = 0 ; while (hh <= tt){ PII t = q[hh ++]; for (int i = 0 ; i < 8 ; ++ i){ int a = t.x + dx[i], b = t.y + dy[i]; if (a < 0 || a >= n || b < 0 || b >= m) continue ; if (g[a][b] == '*' ) continue ; if (dist[a][b] != -1 ) continue ; if (g[a][b] == 'H' ) return dist[t.x][t.y] + 1 ; q[++ tt] = {a, b}; dist[a][b] = dist[t.x][t.y] + 1 ; } } return -1 ; } int main () { scanf ("%d%d" , &m, &n); for (int i = 0 ; i < n; ++ i) scanf ("%s" , g[i]); printf ("%d\n" , bfs ()); return 0 ; }
python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 from collections import dequedef main (): m, n = map (int , input ().split()) g = [input () for _ in range (n)] dist = [[-1 ] * m for _ in range (n)] dx = [-2 , -1 , -1 , -2 , 2 , 1 , 1 , 2 ] dy = [-1 , -2 , 2 , 1 , -1 , -2 , 2 , 1 ] def bfs (): sx, sy = 0 , 0 for i in range (n): for j in range (m): if g[i][j] == 'K' : sx, sy = i, j break q = deque([[sx, sy]]) dist[sx][sy] = 0 while q: x, y = q.popleft() for i in range (8 ): a, b = x + dx[i], y + dy[i] if a < 0 or a >= n or b < 0 or b >= m: continue if g[a][b] == '*' : continue if dist[a][b] != -1 : continue if g[a][b] == 'H' : return dist[x][y] + 1 q.append([a, b]) dist[a][b] = dist[x][y] + 1 return -1 print (bfs()) if __name__ == '__main__' : main()
这里注意问题的转化,要有建图思想,转化为图的搜索问题
注意确定路径范围的上限
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <iostream> #include <algorithm> #include <cstring> #include <cstdio> using namespace std;const int N = 200010 ;int n, k;int q[N];int dist[N];int bfs () { int hh = 0 , tt = 0 ; q[0 ] = n; memset (dist, -1 , sizeof dist); dist[n] = 0 ; while (hh <= tt){ int t = q[hh ++]; if (t == k) return dist[k]; if (t + 1 < N && dist[t + 1 ] == -1 ){ q[++ tt] = t + 1 ; dist[t + 1 ] = dist[t] + 1 ; } if (t - 1 >= 0 && dist[t - 1 ] == -1 ){ q[++ tt] = t - 1 ; dist[t - 1 ] = dist[t] + 1 ; } if (2 * t < N && dist[2 * t] == -1 ){ q[++ tt] = 2 * t; dist[2 * t] = dist[t] + 1 ; } } return -1 ; } int main () { scanf ("%d%d" , &n, &k); printf ("%d\n" , bfs ()); return 0 ; }
python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from collections import dequedef main (): b = 200010 n, k = map (int , input ().split()) dist = [-1 ] * b def bfs (): q = deque([n]) dist[n] = 0 while q: t = q.popleft() if (t == k): return dist[k] if (t - 1 >= 0 and dist[t - 1 ] == -1 ): q.append(t - 1 ) dist[t - 1 ] = dist[t] + 1 if (t + 1 < b and dist[t + 1 ] == -1 ): q.append(t + 1 ) dist[t + 1 ] = dist[t] + 1 if (2 * t < b and dist[2 * t] == -1 ): q.append(2 * t) dist[2 * t] = dist[t] + 1 return -1 print (bfs()) if __name__ == '__main__' : main()