• home
  • about
  • 全ての投稿
  • ソフトウェア・ハードウェアの設定のまとめ
  • 分析関連のまとめ
  • ヘルスケア関連のまとめ
  • 生涯学習関連のまとめ

dfs(depth-first search)

date: 2021-04-18 excerpt: dfs(depth-first search)について

tag: algorithmdfsdepth-first search


dfs(depth-first search)について

  • 深さ優先探索
  • カスタマイズすることでノードがつながった数のカウント等にも使える
  • 四畳半で使われる畳(一畳と半畳の畳)で床を敷き詰めるパターンの全探索等
  • ネットワーク中の閉路の数を求める

具体的なサンプルコード

  • i -> 初期ノード
  • arg -> 何らかの挙動のオプションや累積値
  • vis -> すでに訪れたところをスキップするオプション

テンプレート1(再帰あり)

import sys; sys.setrecursionlimit(10**9)

vis = set()
def dfs(i, arg):
    if i in vis:
        return
    vis.add(i)
    for j in G[i]:
        dfs(j, arg)

テンプレート2(再帰なし, 再帰なしのほうが早い)

def dfs(i, arg):
    que = collections.deque([(i, arg)])
    while que:
        i, arg = que.pop()
        if i in vis:
            continue
        vis.add(i)
        for j in G[i]:
            que.append( (j, arg) )

例; 具体的な例題とその回答

  • Finding an Exit from a Maze
  • Adding Exits to a Maze

例; AtCoder Typical Contest 001; A - 深さ優先探索

問題

  • AtCoder Typical Contest 001; A - 深さ優先探索

解説

  • 典型的なdfsによるソリューション

解答

  • colab

例; dfsを適応するとテンプレート的に解ける問題

問題

  • AtCoder Beginner Contest 197; B - Visibility

解答

  • colab

例; 畳の埋めるパターンをdfsを用いて全探索する

問題

  • AtCoder Beginner Contest 196; D - Hanjo

解答

  • colab

例; queでは解くことができなく再帰でのみ解くことが可能な例

問題

  • 第8回日本情報オリンピック 予選(過去問); D - 薄氷渡り

解説

  • 状態を記述するデータを次の処理に渡す必要があるが、処理が終わったら戻す必要がある(バックトラック法の一種)
  • このデータが大きいとき、queでは参照後リセットを行うというプロセスが記述できないので、必ず再帰として書く必要がある
  • 木dpの一種のようにも思える

解答

  • colab

例; 閉路の検出

問題

  • AtCoder Regular Contest 037; B - バウムテスト

解説

  • 閉路を含む = 一つのノードに複数個の参照がある, と読み替えることができる
  • たどったかどうかを保存するテーブルを工夫し、一つのノードにどれくらい参照があったかをカウントすることで実現できる

解答

  • colab

例; 探索のパターンの全列挙

  • AtCoder Beginner Contest 054; C - One-stroke Path

解説

  • グローバルなvisitedのような変数でたどってきた経路を管理するのではなく、queにたどってきた経路を入れ込むことで、ユニークな経路を列挙することができる

解答

import collections
import itertools
import copy
N,M=map(int,input().split())

G=collections.defaultdict(list)
for _ in range(M):
    a,b = map(int,input().split())
    a-=1;b-=1
    G[a].append(b)
    G[b].append(a)

que = collections.deque([[0, []]])

ans = 0
while que:
    i, memo = que.pop()
    if i in memo:
        continue
    memo.append(i)
    if len(memo) == N:
        ans += 1
        continue
    for j in G[i]:
        que.append( (j, copy.copy(memo)) )
print(ans)

例; 距離が偶数か奇数かで塗り分ける

問題

  • AtCoder Beginner Contest 126; D - Even Relation

解説

  • 2点(a,b)の頂点の距離はある任意の点xからの距離dがある時, da + db - 2dxになる
  • この距離の声質を利用して任意の点からのdfsでの距離を記録しておき、偶数か、奇数かで塗り分ける

解答

import sys; sys.setrecursionlimit(10**9)

N = int(input())
G = [[] for _ in range(N)]

for _ in range(N-1):
    u,v,w=map(int,input().split())
    u-=1; v-=1
    G[u].append( (v,w) )
    G[v].append( (u,w) )

vis = set()
mem = dict()
def dfs(i, acc):
    if i in vis:
        return
    vis.add(i)
    mem[i] = acc
    for j, w in G[i]:
        dfs(j, acc+w)
dfs(0, 0)

for i in range(N):
    if mem[i]%2 == 0:
        print(0)
    else:
        print(1)

例; 接続したノードの数のカウントアルゴリズム

擬似コード

擬似結果



algorithmdfsdepth-first search Share Tweet