AtCoder Beginner Contest 245 G問題メモ

AtCoder Beginner Contest 245

今年度も終わりだね

G - Foreign Friends

問題

  • $N$ 頂点 $M$ 辺の重み付き無向グラフが与えられる
  • 頂点 $i$ には値 $A_i$ が書かれている
  • $N$ 頂点中 $L$ 頂点 $B_{1},B_{2},...,B_{L}$ は「特別な頂点」である
  • 各頂点について、「自分と異なる値が書かれている特別な頂点のうち、最も近いもの」への最短距離を求めよ
  • $2 \le N \le 10^5$
  • $1 \le M \le 10^5$

解法

Dijkstraを基本として、2種類のカスタマイズを行う。

複数始点Dijkstra

普通、Dijkstraといえば「単一始点」の最短経路探索アルゴリズムだが、

  • 始点がいっぱいある
  • 各頂点は、どれでもいいから「最寄りの」始点からの最短距離さえわかればよい

という場合、最初にキューに突っ込む始点を複数個にしても問題ない。

この問題では、$L$ 個の特別な頂点を始点とし、全頂点への最短距離を求めればよい。

上位k番目までを求める

単なる複数始点Dijkstraで最短距離を求めても、実は始点と終点の値が同じだった!ということがあり得る。

そこで「始点に書かれた値」でグループ化して、上位2グループまでの最短距離を求めれば、 どちらかのグループは終点の値と異なるので、正しい答えとして採用できる。

●特別な頂点、○普通の頂点

❶---1---③---1---②---1---①---4---❸      
❷---3---'
❸---5---'
         :       :       :
1st    (1,❶)   (2,❶)   (3,❶)    (最短距離,始点グループ)
2nd    (3,❷)   (4,❷)   (4,❸)
答え    1        2        4

1stが自身と同じ値の始点グループだったら、2ndを採用すればよい。

このようにしても、計算量は、上位 $k$ グループまでを保持するとして(今回は $k=2$)、通常のDijkstraの $k$ 倍という定数倍で収まる。

ただし、通常のDijkstraで行うような枝刈りは、このカスタマイズにおいても行う必要がある。

  • キューから取り出された時点で上位 $k$ 以内でない場合、スキップし、無駄に隣接頂点を調べない
  • キューに入れようとする時点で既に上位 $k$ 以内でない場合、スキップし、無駄な値を入れない
実装について

上位 $k$ 番目まで保持するDijkstraにおいては、以下のサイトで言及されているDijkstraの2種類の実装のうち、 1番目を元にした方が書きやすいかも。

というのも、暫定距離配列 dist をいつ更新するかにおいて、

  • 1番目はキューから取り出されたとき
  • 2番目は自身がキューに入れられるとき

なので、2番目は後から更新されることがあり得る。(1番目は確定してからdistに入れるので「暫定」でもない)

後から更新されうると、新しい値で dist をどのように更新すべきかについて

  • 上位 $k$ 以内に既に自グループがある場合、その値との比較で小さければ更新
  • 上位 $k$ 以内に自グループがない場合、その中で最も劣る値との比較で小さければそいつを追い出して更新

とせねばならず、常に「上位 $k$ 以内の最も劣る値」を取得できなければならない。
ソートなどに計算量を費やすことになり、大変である。

一方、1番目の実装であれば、$k$ 回まで取り出された順に確定していけばよいので、実装も計算量も楽になる。

最短距離のみのDijkstraなら、2番目の実装の方が、キューに突っ込まれる要素を抑えられ、 チェック済みかどうかを管理する配列も持たなくていいのでメモリや計算量的に優しいが、、、 上位 $k$ 番目までを求める場合はそうとは限らないんだね。

Python3

programming_algorithm/contest_history/atcoder/2022/0326_abc245.txt · 最終更新: 2022/03/30 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0