深さ優先探索は、幅優先探索と並べて紹介されることが多いですが、実際の使用場面でいうと深さ優先探索が圧倒的に多い印象です。
そんな深さ優先探索についてアルゴリズムをまとめます。
①組合せアルゴリズムへの適用
・ナップサック問題への適用
・編集距離への適用
②グラフアルゴリズムへの適用
・グラフ探索
・s-tパス探索
・トポロジカルソート
・ベルマンフォード法
・ワーシャルフロイド法
・フォードファルカーソン法
#①組合せアルゴリズムへの適用
###ナップサック問題への適用
N個の品物の重さと価値をweight[i], value[i]とし、ナップサックに入れられる重さの総和をWとするときに、ナップサックに入れられる価値の総和の最大値を求める。
# ナップサック問題を解く動的計画法
# 計算量 :O(NW)
# アルゴリズム定義
def knapsack():
dp = [[0 for number in range(0, W+1)] for i in range(0, N+1)]
for i in range(0, N):
for w in range(0, W+1):
if w - weight[i] >= 0:
dp[i+1][w] = max(dp[i][w], dp[i][w-weight[i]] + value[i])
print(dp[N][W])
# 入力受取
N, W = map(int, (input().split()))
weight = list(map(int, input().split()))
value = list(map(int, input().split()))
# アルゴリズム実行
knapsack()
# 入力
# 6 10
# 2 1 3 2 1 5
# 3 2 6 1 3 85
# 出力
# 96
###編集距離への適用
2つの文字列S, Tがあり、「1文字変更」「1文字削除」「1文字追加」の処理をSがTになるまで繰り返し施すとし、必要な処理回数の最小値を求める。
# 編集距離を求める動的計画法
# 計算量 :O(|S||T|)
# アルゴリズム定義
def editDistance(S, T):
dp = [[10000 for number in range(0, len(T)+1)] for i in range(0, len(S)+1)]
dp[0][0] = 0
for i in range(len(S)+1):
for j in range(len(T)+1):
if i > 0 and j > 0:
if S[i-1] == T[j-1]:
dp[i][j] = dp[i-1][j-1]
else:
dp[i][j] = dp[i-1][j-1]+1
if i > 0:
dp[i][j] = min(dp[i][j], dp[i-1][j]+1)
if j > 0:
dp[i][j] = min(dp[i][j], dp[i][j-1]+1)
print(dp[len(S)][len(T)])
# 入力受取
S = input()
T = input()
# アルゴリズム実行
editDistance(S, T)
# 入力
# kintoun
# nyoibou
# 出力
# 6
#②グラフアルゴリズムへの適用
###グラフ探索
N個の頂点とM個の辺からなる有向グラフGにおいて、始点sから各頂点に接続可能かを判定する。
# グラフ探索:深さ優先探索
# アルゴリズム定義
def DFS(G, v):
seen[v] = True
for next_v in range(len(G[v])):
if seen[G[v][next_v]]:
continue
DFS(G, G[v][next_v])
# 入力受取
N, M = map(int, input().split())
# グラフ生成
G = [[] for i in range(N)]
for j in range(M):
a, b = map(int, input().split())
G[a].append(b)
# メモ準備
seen = [False for i in range(N)]
# アルゴリズム実行
s = 0
DFS(G, s)
# 結果出力
print(seen)
# 入力
# 6 8
# 0 1
# 0 4
# 0 5
# 1 2
# 2 4
# 2 5
# 3 4
# 3 5
# 出力
# [True, True, True, False, True, True]
###s-tパス探索
N個の頂点とM個の辺からなる有向グラフGにおいて、s-tとなるパスが存在するかを判定する。
# グラフ探索:s-tパスを求める
# 計算量:O(|V|+|E|)
# アルゴリズム定義
def dfs(G, v):
seen[v] = True
for next_v in range(len(G[v])):
if seen[G[v][next_v]]:
continue
dfs(G, G[v][next_v])
# 入力受取
N, M, s, t = map(int, input().split())
# グラフ生成
G = [[] for i in range(N)]
for j in range(M):
a, b = map(int, input().split())
G[a].append(b)
# メモ準備
seen = [False for k in range(N)]
# アルゴリズム実行
dfs(G, s)
# 結果出力
if seen[t]:
print("Yes")
else:
print("No")
# 入力
# 8 12 2 7
# 0 1
# 0 2
# 0 3
# 1 2
# 1 5
# 2 4
# 3 4
# 3 7
# 4 6
# 5 4
# 5 6
# 6 7
# 出力
# Yes
###トポロジカルソート
N個の頂点とM個の辺からなる有向グラフGにおいて、各頂点を辺の向きが一方向になるように順序つけて並び替える。
# グラフ探索:トポロジカルソート
# 計算量:O(|V|+|E|)
# アルゴリズム定義
def rec(G, v, order):
seen[v] = True
for next_v in range(len(G[v])):
if seen[G[v][next_v]]:
continue
rec(G, G[v][next_v], order)
order.append(v) # 再帰関数を抜ける瞬間(帰りがけ順)のvを順に記憶
# 入力受取
N, M = map(int, input().split())
# グラフ生成
G = [[] for i in range(N)]
for j in range(M):
a, b = map(int, input().split())
G[a].append(b)
# メモ準備
seen = [False for i in range(N)]
# トポロジカルソート格納リスト生成
order = []
# アルゴリズム実行
for v in range(N):
if seen[v]:
continue
rec(G, v, order)
# 結果出力
order.reverse() # 再帰関数を抜けた順を逆転することでトポロジカルソートを取り出す
print(order)
# 入力
# 8 12
# 4 2
# 4 6
# 4 1
# 1 6
# 2 7
# 6 7
# 1 3
# 3 7
# 7 0
# 2 5
# 0 5
# 3 0
# 出力
# [4, 2, 1, 3, 6, 7, 0, 5]
###ベルマンフォード法
N個の頂点とM個の辺からなる負の閉路を含む有向グラフGにおいて、始点sから各頂点への最短経路を計算する。
# ベルマン・フォード法
# 計算量:O(|V||E|)
# アルゴリズム定義
def BellmanFord(G, s):
exist_negative_cycle = False
dist = [10000 for i in range(len(G))]
dist[s] = 0
for i in range(len(G)):
update = False
for v in range(len(G)):
if dist[v] == 10000:
continue
for e in range(len(G[v])):
if dist[G[v][e][0]] > dist[v] + G[v][e][1]:
dist[G[v][e][0]] = dist[v] + G[v][e][1]
update = True
if update == False:
break
if i == len(G)-1 and update == True:
exist_negative_cycle = True
if exist_negative_cycle == True:
print("NEGATIVE CYCLE")
else:
for v in range(len(G)):
if dist[v] < 10000:
print(dist[v])
else:
print("INF")
# 入力受取
N, M, s = map(int, input().split())
# グラフ生成
G = [[] for i in range(N)]
for i in range(M):
a, b, w = map(int, input().split())
G[a].append([b, w])
# アルゴリズム実行
BellmanFord(G, s)
# 入力
# 6 12 0
# 0 1 3
# 0 3 100
# 1 2 50
# 1 3 57
# 1 4 -4
# 2 3 -10
# 2 5 100
# 3 1 -5
# 4 2 57
# 4 3 25
# 4 5 8
# 2 4 -5
# 出力
# 0
# 3
# 53
# 24
# -1
# 7
###ワーシャルフロイド法
N個の頂点とM個の辺からなる有向グラフGにおいて、全頂点間について最短路長を求める。
# ワーシャル・フロイド法
# 計算量:O(|V|^3)
# アルゴリズム定義
def WarshallFloyd(dp):
for i in range(len(dp[1])):
for j in range(len(dp[0])):
for k in range(len(dp[0])):
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j])
exist_negative_cycle = False
for v in range(len(dp[0])):
if dp[v][v] < 0:
exist_negative_cycle = True
if exist_negative_cycle:
print("NEGATIVE CYCLE")
else:
for i in range(len(dp[0])):
for j in range(len(dp[0])):
if j:
print(" ", end='')
if dp[i][j] < 5000:
print(dp[i][j], end='')
else:
print(10000, end='')
print("")
# 入力受取
N, M = map(int, input().split())
# dp生成
dp = [[100000 for i in range(N)] for j in range(N)]
for e in range(M):
a, b, w = map(int, input().split())
dp[a][b] = w
for v in range(N):
dp[v][v] = 0
#アルゴリズム実行
WarshallFloyd(dp)
# 入力
# 6 9
# 0 1 3
# 0 2 5
# 1 2 4
# 1 3 12
# 2 3 9
# 2 4 4
# 3 4 7
# 3 5 2
# 4 5 8
# 出力
# 0 3 5 14 9 16
# 10000 0 4 12 8 14
# 10000 10000 0 9 4 11
# 10000 10000 10000 0 7 2
# 10000 10000 10000 10000 0 8
# 10000 10000 10000 10000 10000 0
###フォードファルカーソン法
N個の頂点とM個の辺からなる有向グラフGにおいて、各辺eが容量c(e)を持つ場合に、始点sから終点tへの最大流問題を計算する。
# フォード・ファルカーソン法
# クラス定義(アルゴリズム定義)
class FordFulkerson():
def __init__(self):
self.INF = 10000
self.seen = [False for i in range(N)]
def rev(self, G, g, v):
for i in range(len(G[g])):
if G[g][i][0] == v:
return i
def fodfs(self, G, v, t, f):
if v == t:
return f
self.seen[v] = True
for i in range(len(G[v])):
if self.seen[G[v][i][0]]:
continue
if G[v][i][1] == 0:
continue
flow = self.fodfs(G, G[v][i][0], t, min(f, G[v][i][1]))
if flow == 0:
continue
G[v][i][1] -= flow
k = self.rev(G, G[v][i][0], v)
G[G[v][i][0]][k][1] += flow
return flow
return 0
def solve(self, G, s, t):
res = 0
while True:
for i in range(len(G)):
self.seen[i] = 0
flow = self.fodfs(G, s, t, 10000)
if flow == 0:
return res
res += flow
return 0
# 入力受取
N, M = map(int, input().split())
# グラフ生成
G = [[] for i in range(N)]
for i in range(M):
u, v, c = map(int, input().split())
G[u].append([v, c])
for j in range(len(G[u])):
if len(G[v]) > j:
if G[v][j][0] == u:
break
G[v].append([u, 0])
# 初期条件
s, t = 0, N-1
# オブジェクト作成
ff = FordFulkerson()
# アルゴリズム実行
ff.solve(G, s, t)
# 入力
# 6 9
# 0 1 5
# 0 3 5
# 1 2 4
# 1 3 37
# 2 4 56
# 2 5 9
# 3 2 3
# 3 4 9
# 4 5 2
# 出力
# 9