グラフ, 格子状のグラフ, 次元拡張グラフを同じコードで扱う抽象化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