6.4 すべての頂点対に対する 最短経路問題 Dijkstra は出発点を1つ決めていたが、 全頂点間の最短路は? ただし,n: 頂点の数,e: 辺の数 Dijkstra のアルゴリズムを全ての頂点に対し て適用する: 隣接行列: O(n3) 隣接リスト: O(ne log n) Floyd のアルゴリズム 初期化: A[i,j] = C[i,j] for all i ≠ j 以下のように A を更新する: Ak[i,j] = min { Ak-1[i,j], Ak-1[i,k]+Ak-1[k,j] } Ak[i,j]: k 番目以下のノードのみを 通過する i から j への最短距離 k i j Floyd のアルゴリズム 8 1 2 2 Ak[i,j] = min { Ak-1[i,j] Ak-1[i,k]+Ak-1[k,j] } 3 2 3 5 k=1 A0 1 2 3 1 0 3 ∞ 2 8 0 2 3 5 ∞ 0 A1 1 2 3 1 0 3 ∞ k=2 A2 1 2 3 1 0 3 5 2 8 0 2 3 5 8 0 2 7 0 2 3 5 8 0 k=3 2 8 0 2 3 5 8 0 A3 1 2 3 1 0 3 5 問題 次のグラフにおいて,Floydのアルゴリズム の実行の様子を書きなさい 8 1 2 3 5 2 3 for i = 1 to n do { for j = 1 to n do { A[i,j] = C[i,j]; } } for i=1 to n do { A[i,i] = 0; } for k = 1 to n do { for i = 1 to n do { for j = 1 to n do { if(A[i,k] + A[k,j] < A[i,j]){ A[i,j] = A[i,k] + A[k,j]; } } } ※ Ak[i,k] = Ak-1[i,k] および Ak[k,j] = Ak-1[k,j] } なので、k ステップ目で A[i,k] および A[k,j] は 変化しない Î 同じ行列を書き換えても影響を与えない 問題 前スライドを言葉で説明しなさい 問題 Floydのアルゴリズムの計算量を書きなさい FloydのアルゴリズムとDijkstraのアルゴリズム, どちらの計算量が良いでしょうか? Warshall のアルゴリズム 経路があるかどうかを知りたいだけの場合 Ak[i,j] = i から j まで k 番目以下のノードのみを 通過した経路が存在するかどうか Ak[i,j] = Ak-1[i,j] ∨ (Ak-1[i,k] ∧ Ak-1[k,j]) ※Floyd のアルゴリズムの距離が真偽値に変 えただけ (Floyd のアルゴリズム以前に発見 された) 応用例:有向グラフの中心 グラフの G=(V,E) の「なるべく真ん中」 頂点 v の離心率 = max { w から v への最短距離} w∈V G の中心 = 離心率が最小の頂点 有向グラフの中心の例 a 頂点 離心率 1 b 1 2 2 c d 3 4 5 e a ∞ b 6 c 8 d 5 e 7 問題 次の有向グラフの中心を書きなさい a 1 b 1 2 2 c d 3 1 1 e 計算量 1. Floyd のアルゴリズムを使用して、各頂点 間の最小距離を求める O(n3) 2. 各列 i の最大コストを求める = 頂点 i の離心率 O(n2) 3. 離心率最小の頂点を求める O(n) 問題 2個前の有向グラフの中心を求める計算過程です. 前スライドの1,2,3が該当する部分を示しなさい a b c d e a 0 1 3 5 7 b ∞ 0 2 4 6 c ∞ 3 0 2 4 d ∞ 1 3 0 7 e ∞ 6 8 5 0 max ∞ 6 8 5 7 6.5 有向グラフのなぞり 木の行きがけ順のなぞりを一般化した深さ優先探索 1. 最初は有向グラフ G の全ての頂点に unvisited という マークがついているとする 2. G の中の1つの頂点 v を出発点として定める A) 頂点v には visited というマークを付ける B) 深さ優先探索を再帰的に使い、v に隣接する、かつ、 unvisited とマークされている頂点を1つずつ調べる C) 頂点v から行ける頂点を全部訪問し終わった時点で v に関する探索は終了する 3. まだ unvisited とマークされている頂点が残っていれば、 そのうちの1つを新しい出発点として選び、2.A~2.C を繰 り返す 深さ優先探索の呼び出し count = 0; //大域変数.頂点の訪問番号 for(v = 0; v < n; v++) //初期化 mark[v] = unvisited; for(v = 0; v < n; v++) //呼び出し if(mark[v] == unvisited) dfs(v); 注:C言語なので頂点番号が1個ずれています 問題 前スライドを言葉で説明しなさい 深さ優先探索の関数 void dfs(int v){ mark[v] = visited; dfnumber[v] = count; count++; /* L[v] は v の隣接リスト */ for(L[v] 上の各頂点 w){ if(mark[w] == unvisited) dfs(w); } } ※ 実際には dfs 内で v に対して行う処理がつけ 加わる 問題 前スライドを言葉で説明しなさい 深さ優先探索の計算量 e 本の辺,n (≦e) 個の頂点を持つグラフに対 して深さ優先探索を行ったとする • dfs(v) が呼ばれると、mark[v] は直ちに visited に変更される • mark[v] が visited であるときは dfs(v) は呼 ばれない よって,計算量はO(e) 深さ優先探索の例 頂点の訪問番号 2 6 F B 1 A 5 E 4 7 G 3 D C 問題 前スライドの例に深さ優先探索を行った. 実行の様子を完成しなさい 呼び出し側で,dfs(A) 関数内で,dfs(B) 関数内で,dfs(C) 関数内で,dfs(D) 呼び出し側で,dfs(?) 関数内で,? 深さ優先の極大森 dfs において unvisited な頂点につながっていた 弧(辺)からなる木の集合(森) 2 6 F B 1 A 5 E 4 7 G 3 D C 残りの弧:後退弧,先行弧,交差弧 w が v の子孫 ⇔ df#[v] ≦ df#[w] ≦ df#[v] + v の子孫数 ただし, df#[u] は頂点uの訪問番号 6 7 G 1 A 後 退 弧 B 4 3 D 交差弧 後退弧 E 交差弧 F 5 2 交差弧 先行弧: 番号が 小 Î 大 後退弧:番号が 大 Î 小 交差弧:番号が 大 Î 小 C 交差弧 ※ 小 Î 大となる交差弧はあり得ない (木の弧か先行弧になってしまう) 問題 次の有向グラフにおいて,深さ優先の極大森を 求めなさい.頂点には訪問番号を書きなさい. さらに,後退弧,先行弧,交差弧も求めなさい. F B A G D C E v の子孫の数の求め方 void dfs(int v){ mark[v] = visited; dfnumber[v] = count; count++; children[v] = 0; /* L[v] は v の隣接リスト */ for(L[v] 上の各頂点 w){ if(mark[w] == unvisited){ dfs(w); children[v] += (1 + children[w]); } 注:括弧がなくても正常に動きます. } ただし,実際には括弧をつけた方がいいです. }
© Copyright 2024 Paperzz