ツバサの備忘録

主に備忘録代わりに精進記録を載せていくつもりです。

九州大学プログラミングコンテスト2018 D - Novelist

問題
提出コード

解法

王都から都市X_{i}へ移動する依頼をうけるとき、その後に王都からX_{j}へ移動する依頼を受けるには、一旦X_{i}から王都へ戻る依頼を受ける必要があります。
ということで、王都を出て、また王都へ戻る期間を、金貨が2枚もらえる1つの区間と見て、期間が重複しないように区間を選び、もらえる金貨の枚数を最大にすることを考えます。
すると、これは区間スケジューリングと同じことを考えることになります。
さて、あとは区間を作成していけばよいのですが、作成できる全ての区間を考慮していくと、区間の個数が大きくなりすぎてしまい、時間内に解くことができなくなってしまいます。ので、王都を出発するM個の依頼について、最も効率が良くなるような、王都へ戻る依頼をL個の中から探し出し、それを1つの区間とすることで、たかだかM個の区間だけに絞ることができるようになります。
王都を出発してとある都市へ行く、i番目の依頼について見た時、ペアとなる依頼は、

  • Y_{j} = X_{i}およびB_{j} \gt A_{i} + T_{X_{i}}を満たす依頼の中で、B_{j}が最小のもの

となります。
この依頼は、L個の依頼を、Y_{j}で分類し、それぞれB_{j}の昇順でソートしておくことで、二分探索によって求めることができます。
こうしてできたペアは、[A_{i},B_{j} + T_{X_{i}} ]という区間になります。
また、もし条件を満たすペアが存在しなかった場合、[A_{i}, \infty]という区間を作成しておきます。これは、最終的に王都に戻る必要がないので、そのパターンに対処するためです(戻れる依頼が存在する場合は、間違いなく戻った方が得をするので、この処理を行うのは、ペアとなる依頼が存在しなかった場合のみです)。
あとは、作成した区間について、区間の後ろ側で昇順ソートをし、区間スケジューリングを行っていけば答えを求めることができます。
現在、受けると決めた依頼のうち、最後に完了する時刻をtとすると、ソートした区間からについて前からみていき、現在みている区間[X,Y]であったときに、

  • t \lt XかつY \neq \inftyのときに、金貨を2枚獲得、tYに更新

  • t \lt XかつY = \inftyのときに、金貨を1枚獲得、t\inftyに更新

という操作を行っていけば、答えを求めることができます。

感想

区間でまとめることができる、ということに気づくと、うまく区間スケジューリングに帰着させることができます。
久々に区間スケジューリングへ帰着させる問題を解きましたが、結構すんなりと考察することができたのでよかったです!
実装も特にバグを含まなかったので安心です。