091 D - Coloring Edges on Tree¶
- created: 2022-12-21 wed
- ご意見・ご要望はissue・プルリク用のGitHubまで
- 競技プログラミングのためのF#入門
- GitHub上の対応ディレクトリ
- 公式ページ
- 要点: アルゴリズムの検討・実装
入出力¶
1 2 3 | |
解説¶
公式解説ではBFSで解説していました. ここでDFSで木を走査します.
隣接リスト生成¶
問題の指示によってi本目の辺はa_iとb_iを含むとしているため, 隣接リストを作るときに辺の番号も同時に割り振ります. ループのときの配列の添字と混同しないようにここでedgeのeを採用して記述します.
1 2 3 4 | |
ここでは配列の中身をリストにしました. 問題90ではResizeArrayよりリストの方が速かったため, 何となくリストにしています. 何が速いかはきちんと検証するべきです.
DFS¶
再帰と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しかありません. 配列にせず, 最終的に返すべき改行区切りの文字列を生成してもいいでしょう.