グラフ, 格子状のグラフ, 次元拡張グラフを同じコードで扱う抽象化BFS, DijkstraのC++14実装
これすごく悩んでいたんですけど, 新しい実装法を考えたらスッキリしたのでまとめます.
先駆者がいたらごめん, 勝手ににう式グラフ抽象化って言っちゃおうかな
# 意思
DijkstraやBFSのライブラリを書いておきたい!
# 問題点
- 同じアルゴリズムのライブラリを, 種類の違うグラフに対して一つずつ書くのは本当にイヤ
- 格子状のグラフをわざわざ,
std::vector<std::vector<int>> G
に構築するのは定数倍があるのでイヤ. - 問題を解くために次元拡張したグラフをわざわざ構築するのは, キレイじゃない
struct edge
がC++においてはなんかイヤ, 隣接する頂点がint to
で固定しないとダメだったりでイヤ(Rustのtraitとかあるならまだしも 個人差あり)- そもそも
struct edge
を毎回書くのもイヤ(競プロなので) - 隣接リストを毎回
std::vector<edge>
で返すのもキレイじゃない, できるだけ無駄はなくしたい.
どうしよう(欲求が多すぎる)
# 解決
抽象化しにくいのはなぜかというと, struct edge
と隣接行列の保持の仕方がグラフの種類ごとに違うからです. なので, 方針としては
struct edge
は, ライブラリに触れさせない- 隣接行列は, 直接ライブラリに触れさせない
という点を実現できれば良いです. ですが, 隣接行列をstd::vector<なんたら>
で返す関数を返すのも美しくありません. なので, これを関数で受けることにします.
具体的には,
- BFS関数は,
delta
という隣接行列を生成する関数を受け取ります. - 隣接する頂点を列挙する時は,
delta
に, 現在の頂点$v$と, $v$と隣接する頂点を引数にとる, 探索をする関数を渡します. delta
が隣接する頂点を, 引数で渡された関数の引数に入れて探索をしてもらいます.- できる!
Dijkstraの場合には, delta
に渡す関数を(V t /* 隣接頂点 */, W cost /* 辺のコスト */)
にしておけばOKですね.
頂点の抽象化は, index(V) -> int
があればいいです.
# コード
# BFS
|
|
# Dijkstra
|
|
# How To Use
格子グラフに関しては, こんな感じに予めライブラリを書いておくと楽です.
|
|
これで, Maze Masterは以下のように解くことができます.
|
|
また, 次元拡張グラフを扱う E - Two Currenciesは, こんな感じで.
|
|
C++14ならジェネリックラムダあるので, auto func
って書き殴れるのでいいですね.
# 提出ページ
- 格子グラフのBFS: D - Maze Master - 提出 #11773027 - AtCoder Beginner Contest 151 ちょっとコード違うけど許して
- 格子グラフの01dial(01-bfs): A - Range Flip Find Route - 提出 #11773947 - AtCoder Grand Contest 043
- 次元拡張グラフのDijkstra: E - Two Currencies - 提出 #12401080 - AtCoder Beginner Contest 164