G - Counting Shortest Paths 解説
by
suisen
公式解説のStep1を$O(N+M)$時間で行う方法
グラフ \(G(V,E)\) の補グラフにおける、頂点 \(s\ (=0)\) から各頂点 \(v\) までの最短距離 \(\mathtt{dist}(v)\) の列挙を \(O(\lvert V\rvert + \lvert E\rvert)\) 時間で行う方法を説明します。
方針は公式解説と概ね同じです。
頂点 \(v\) に隣接する頂点の集合を \(N(v)\) とします。各頂点 \(v\) に対する \(N(v)\) の計算は \(O(\lvert V\rvert + \lvert E\rvert)\) 時間で行えます。
次の擬似コードのように各頂点 \(v\) に対する \(\mathtt{dist}(v)\) を列挙します。赤字の部分が公式解説からの変更点であり、重要なのは、現在見ている頂点に隣接しているかどうかを表すフラグを管理する配列 \(\mathtt{mark}\) を持って適切に更新することです。
- \(\quad\)\(\mathtt{//}\) 最短距離の暫定値
- \(\quad\)\(\mathtt{dist}(s)\gets 0\)
- \(\quad\)\(\mathtt{for}\ v\in V\setminus\lbrace s\rbrace\colon\)
- \(\quad\quad\)\(\mathtt{dist}(v)\gets \infty\)
- \(\quad\)\(\mathtt{//}\) 公式解説の \(L\)
- \(\quad\)\(L\gets \mathtt{list}(V\setminus\lbrace s\rbrace)\) \(\mathtt{//}\) \(V\setminus\lbrace s\rbrace\) の要素からなる list (順不同)
- \(\quad\)\(\mathtt{//}\) \(\textcolor{red}{\mathtt{mark}(v)}\colon\) 頂点 \(v\) が現在見ている頂点に隣接しているかどうかを表すフラグ
- \(\quad\)\(\textcolor{red}{\mathtt{for}\ v\in V\colon}\)
- \(\quad\quad\)\(\textcolor{red}{\mathtt{mark}(v)\gets \mathtt{false}}\)
- \(\quad\)\(\mathtt{//}\) BFS に用いる Queue
- \(\quad\)\(Q\gets \mathtt{deque}(\lbrace s\rbrace)\)\(\quad\mathtt{//}\) \(s\) のみからなる \(\mathtt{deque}\)
- \(\quad\)\(\mathtt{while}\ \lvert Q\rvert\gt 0\colon\)
- \(\quad\quad\)\(\mathtt{//}\) 現在見ている頂点
- \(\quad\quad\)\(u\gets Q.\mathtt{pop\_front}()\)
- \(\quad\quad\)\(\mathtt{//}\) \(u\) に隣接している頂点に対して隣接フラグを立てる
- \(\quad\quad\)\(\mathtt{//}\) 各頂点は高々 \(1\) 回ずつしか見られないので、ここの \(\mathtt{for}\) は全体で高々 \(2\lvert E\rvert\) 回しか回らない
- \(\quad\quad\)\(\textcolor{red}{\mathtt{for}\ v\in N(u)\colon}\)
- \(\quad\quad\quad\)\(\textcolor{red}{\mathtt{mark}(v)\gets \mathtt{true}}\)
- \(\quad\quad\)\(\mathtt{//}\) \(L\cap N(u)\)
- \(\quad\quad\)\(L'\gets \mathtt{empty\_list}()\)
- \(\quad\quad\)\(\mathtt{for}\ v\in L\colon\)
- \(\quad\quad\quad\)\(\mathtt{//}\) 計算量改善ポイント: 平衡二分探索木に対する membership クエリを配列アクセスで代替
- \(\quad\quad\quad\)\(\mathtt{if}\ \textcolor{red}{\mathtt{mark}(v)}\colon\)
- \(\quad\quad\quad\quad\)\(\mathtt{//}\) \(\mathtt{mark}(v)\iff v\in N(u)\)
- \(\quad\quad\quad\quad\)\(L'.\mathtt{append}(v)\)
- \(\quad\quad\quad\)\(\mathtt{else}\colon\)
- \(\quad\quad\quad\quad\)\(\mathtt{//}\) \(v\notin N(u)\) であり、つまり \(v\) は \(G\) の補グラフにおいて \(u\) と隣接している
- \(\quad\quad\quad\quad\)\(\mathtt{dist}(v)\gets\mathtt{dist}(u)+1\)
- \(\quad\quad\quad\quad\)\(Q.\mathtt{push\_back}(v)\)
- \(\quad\quad\)\(\mathtt{//}\) \(L\) の更新
- \(\quad\quad\)\(L\gets L'\)
- \(\quad\quad\)\(\mathtt{//}\) \(u\) に隣接している頂点に対して立てた隣接フラグを下ろす (この操作によって全ての頂点 \(v\) について \(\mathtt{mark}(v)\) は \(\mathtt{false}\) となる)
- \(\quad\quad\)\(\textcolor{red}{\mathtt{for}\ v\in N(u)\colon}\)
- \(\quad\quad\quad\)\(\textcolor{red}{\mathtt{mark}(v)\gets \mathtt{false}}\)
各変数に対して適切なデータ構造を用いることで、この手続きは \(O(\lvert V\rvert + \lvert E\rvert)\) 時間となります。
投稿日時:
最終更新:
