Atcoder水色への精選 100 問 幅優先探索(Python)
水色になるための精選100問、
今回は28~33までの「幅優先探索」です
前回↓
深さ優先探索
algo-archit.hatenablog.com
目次
- 28 ALDS_11_C - 幅優先探索
- 29 AtCoder Beginner Contest 007 C - 幅優先探索
- 30 JOI 2011 予選 5 - チーズ
- 31 JOI 2012 予選 5 - イルミネーション
- 32 AOJ 1166 - 迷図と命ず
- 33 AtCoder Beginner Contest 088 D - Grid Repainting
28 ALDS_11_C - 幅優先探索
問題文
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_11_C&lang=ja
dequeと呼ばれる両端キューを用いることで問題を解くことができます
そのノードに訪問したか、そのノードまでの距離をそれぞれ格納するリストを用意しておき、
訪問済みでないノードにたどり着いた場合にキューを追加します
キューがすべてなくなった時の距離が答えです
pythonでの実装例
from collections import deque inf = 10**10 n = int(input()) network = [[] for _ in range(n+1)] length_list = [inf for _ in range(n+1)] visited_list = [0 for _ in range(n+1)] for _ in range(n): inputs = list(map(int,input().split())) u,k = inputs[:2] v = inputs[2:] network[u] += v que = deque() # キューを作成 que.append(1) length_list[1] = 0 while que: node = que.popleft() # 先頭から要素を取得 visited_list[node] = 1 for i in network[node]: if visited_list[i]: continue que.append(i) # visited_list[i] = 1 # 長さ+1をしても現在より短くなる場合は更新 if length_list[i] > length_list[node]+1: length_list[i] = length_list[node]+1 for i in range(1,n+1): if length_list[i] == inf: print(i,-1) else: print(i,length_list[i])
29 AtCoder Beginner Contest 007 C - 幅優先探索
問題文
atcoder.jp
マス目状の問題の場合でも二次元型のリストを使って同じように解くことができます
壁に当たらず、かつ訪問済みでないマスをキューに追加して答えのマスまでの距離を求めます
pythonでの実装例
from collections import deque inf = 10**10 R,C = map(int,input().split()) sy,sx = map(int,input().split()) gy,gx = map(int,input().split()) c = [input() for _ in range(R)] sy -= 1 sx -= 1 gy -= 1 gx -= 1 inf = 10**10 visited = [[0 for i in range(C)] for j in range(R)] length = [[inf for i in range(C)] for j in range(R)] que = deque([]) que.append([sy,sx]) visited[sy][sx] = 1 length[sy][sx] = 0 while que: y,x = que.popleft() for y_move,x_move in [[-1,0],[1,0],[0,-1],[0,1]]: ny = y+y_move nx = x+x_move if visited[ny][nx] or c[ny][nx] == "#": continue que.append([ny,nx]) visited[ny][nx] = 1 if length[ny][nx] > length[y][x]+1: length[ny][nx] = length[y][x]+1 print(length[gy][gx])
30 JOI 2011 予選 5 - チーズ
問題文
atcoder.jp
硬さ1のチーズから順番に食べていけばOKです
順番を辞書に格納しておき、各中継地点までのかかる時間(距離)を合計したものが答えとなります
pythonでの実装例
from collections import deque,defaultdict inf = 10**10 h,w,n = map(int,input().split()) town = [input() for _ in range(h)] factory = defaultdict(int) # スタートと工場の順番を辞書に入れる(スタートを0とする) for i in range(h): for j in range(w): if town[i][j] == "S": factory[0] = [i,j] elif town[i][j] == "X" or town[i][j] == ".": continue else: factory[int(town[i][j])] = [i,j] def bfs(sy,sx,gy,gx): visited = [[0 for j in range(w)]for i in range(h)] length = [[inf for j in range(w)]for i in range(h)] que = deque([]) que.append([sy,sx]) visited[sy][sx] = 1 length[sy][sx] = 0 while que: y,x = que.popleft() for i,j in [[-1,0],[0,1],[0,-1],[1,0]]: ny,nx = y+i,x+j if 0 <= ny < h and 0 <= nx < w and visited[ny][nx]== 0 and town[ny][nx] != "X": que.append([ny,nx]) visited[ny][nx] = 1 length[ny][nx] = length[y][x]+1 return length[gy][gx] total = 0 # 最終的にかかる時間 # 順番に経路をたどっていく for i in range(1,n+1): sy,sx = factory[i-1] gy,gx = factory[i] total += bfs(sy,sx,gy,gx) print(total)
31 JOI 2012 予選 5 - イルミネーション
問題文
atcoder.jp
やっかいな問題に見えますが、移動できる方向が4→6に増えたと考えれば大丈夫です(偶奇の時の場合分けに注意)
今回はマス目の外側に1つ分余分なマスを作り、自由に動き回ったときに、何回壁に当たったかをカウントします
このときのカウント数がイルミネーションする壁面の個数になります
この問題に関しては公式の解説図がとっっっても分かりやすかったので確認してみてください↓
www.ioi-jp.org
pythonでの実装例
from collections import deque inf = 10**10 w,h = map(int,input().split()) field = [[0 for j in range(w+2)]for i in range(h+2)] visited = [[0 for j in range(w+2)]for i in range(h+2)] for i in range(h): field[i+1][1:w+1] = map(int,input().split()) # どの行かで動き方が異なる odd_move = [[-1,0],[-1,1],[0,1],[1,1],[1,0],[0,-1]] even_move = [[-1,-1],[-1,0],[0,1],[1,0],[1,-1],[0,-1]] que = deque([]) que.append([0,0]) visited[0][0] = 1 # 壁にぶつかった回数をカウント collision = 0 while que: y,x = que.popleft() if y%2 == 1: move = odd_move else: move = even_move for i,j in move: ny,nx = y+i,x+j if ny < 0 or ny >= h+2 or nx < 0 or nx >= w+2: continue elif field[ny][nx] == 1: collision += 1 # 壁にぶつかったのでカウント continue elif visited[ny][nx]: continue else: que.append([ny,nx]) visited[ny][nx] = 1 print(collision)
32 AOJ 1166 - 迷図と命ず
問題文
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1166
一見ややこしくてわかりにくい問題ですが、マス目を新しく自分で作り、
縦横の4方向に動くことができるかを壁のリストから参照します
端側の処理だけ注意していれば問題なく解くことができます
pythonでの実装例
from collections import deque inf = 10**10 # 0,0入力が来るまで while True: w,h = map(int,input().split()) if w == 0 and h == 0: break v_wall = [] # 垂直壁 h_wall = [] # 水平壁 for i in range(2*h-1): if i%2 == 0: v_wall.append(list(map(int,input().split()))) else: h_wall.append(list(map(int,input().split()))) visited = [[0 for i in range(w)] for j in range(h)] length = [[inf for i in range(w)] for j in range(h)] visited[0][0] = 1 length[0][0] = 1 que = deque([]) que.append([0,0]) while que: y,x = que.popleft() can_go = [] # 行くことができる方向 # 左確認 if x != 0 and v_wall[y][x-1] != 1: can_go.append([y,x-1]) # 右確認 if x != w-1 and v_wall[y][x] != 1: can_go.append([y,x+1]) # 上確認 if y != 0 and h_wall[y-1][x] != 1: can_go.append([y-1,x]) # 下確認 if y != h-1 and h_wall[y][x] != 1: can_go.append([y+1,x]) if not can_go: continue for ny,nx in can_go: if visited[ny][nx]: continue que.append([ny,nx]) visited[ny][nx] = 1 length[ny][nx] = length[y][x]+1 if length[h-1][w-1] != inf: print(length[h-1][w-1]) else: print(0)
33 AtCoder Beginner Contest 088 D - Grid Repainting
問題文
atcoder.jp
問題文を言い換えると
「最短距離のマス目以外を黒くするにはあと何マス塗りつぶせばいいですか?」
となります
よって答えは、
「全マスの個数H*W - 最短距離 - すでに黒いマスの個数」
です
pythonでの実装例
from collections import deque inf = 10**10 h,w = map(int,input().split()) field = [input() for _ in range(h)] length = [[inf for j in range(w)]for i in range(h)] visited = [[0 for j in range(w)]for i in range(h)] move = [[-1,0],[1,0],[0,-1],[0,1]] que = deque([]) que.append([0,0]) length[0][0] = 1 # 1スタート visited[0][0] = 1 white = 0 for i in range(h): white += field[i].count(".") while que: y,x = que.popleft() for i,j in move: ny,nx = y+i,x+j if ny < 0 or ny >= h or nx < 0 or nx >= w: continue elif field[ny][nx] == "#": continue elif visited[ny][nx] == 1: continue visited[ny][nx] = 1 que.append([ny,nx]) length[ny][nx] = length[y][x] + 1 if length[h-1][w-1] == inf: print(-1) else: print(white-length[h-1][w-1]) # 白の総数から最小距離分引く