競技プログラミングのためのF#入門
1 2 3 |
|
公式解説ではBFSで解説していました. ここでDFSで木を走査します.
問題の指示によってi
本目の辺はa_i
とb_i
を含むとしているため, 隣接リストを作るときに辺の番号も同時に割り振ります. ループのときの配列の添字と混同しないようにここでedge
のe
を採用して記述します.
1 2 3 4 |
|
ここでは配列の中身をリストにしました. 問題90ではResizeArray
よりリストの方が速かったため, 何となくリストにしています. 何が速いかはきちんと検証するべきです.
再帰とfold
で隣接リストを走査します. 今回はArray.zeroCreate N
で初期化した配列の値を再帰の中でゴリゴリ書き換える形で実装します. IntMap
(いわゆる辞書)を使っているHaskell実装もあります.
dfs
関数には親ノード・子ノード・色と書き換える配列を渡せばよいでしょう. 大枠として次のように書けばよいはずです.
1 2 |
|
ではdfs
の中身を考えましょう. まず指定した子ノードがつながっている頂点を取るためGa.[ci]
を取ります. この中から親ノードを排除したいためfilter
をかませます.
1 |
|
これに対して最終的にノード番号とのタプルになるように色を順に割り振ります. dfs
の引数に入っているcolor
以外の色を割り当てるように色を振るため少し工夫します. 前段のfilter
と合わせると要素数の処理が面倒なため, 遅延処理のSeq
を使って力づくで辻褄を合わせます. 結論から言うと次のように書きます.
1 |
|
Seq.zip
はseq
を二つ与えてそれらをタプルにしたseq
を返します.
1 2 3 |
|
二番目のリストの最後の15
が結果で消えている点に注意してください. List.zip
やArray.zip
では「長さが等しいリスト・配列を与えなさい」と怒られます. Seq.zip
なら余計な長さの部分を切り落としてくれるため, Seq.initInfinite
で無限リストを生成し, 無限リスト中の不要な要素をfilter
で切りつつ, 必要なところだけ切り出す都合のいい処理が書けます. あとはこれと入力の配列をfold
で処理します.
1 2 3 |
|
fold
中ではdfs
の引数を子ノード・孫ノードのci
とgci
にずらして, Cq
中で指定した色と更新した配列を与えます.
最後にdfs
の結果から出力用のデータを生成します. 出力では色の数も返す必要があるからです. 出力のXa
から最大値を返せばよく, 例えば次のように書けばよいでしょう.
1 2 3 4 5 |
|
Seq
もArray
もリストのcons
(::
)のような先頭への追加がなく, 配列同士の結合のappend
しかありません. 配列にせず, 最終的に返すべき改行区切りの文字列を生成してもいいでしょう.