問題概要
問題文 → ■
自分の提出 → ■ (35 ms)
一発書きで fastest (2024/08/27)になったのは意外でした.
解法
以下,有理数と言えば非負であることを仮定し,有理数 のような表記はすべて は非負整数で を満たすことを仮定します.また便宜上 という有理数も考え,これらは任意の非負有理数より大きな有理数()であると考えます.
本問題は形式上は「 を近似する」という形になっていますが,より一般に次の形に一般化して解くことができますし,実用上も役に立つと思います.
有理数を引数として bool 値を返す関数 があり,次の意味で単調性があるとする:, とするとき,, かつ任意の有理数 に対して ならば .
このとき有理数集合 は有限集合で, および であるから および が存在する.これらを求めよ.
計算量解析において, の呼び出しは 時間であると仮定します.
まず Stern-Brocot 木をたどれば,次のようなアルゴリズムが考えられます:
- , で初期化する.
- である限り次を行う:
- ならば を に取り換える.
- ならば を に取り換える.
このとき および が出力されます.Stern-Brocot 木の性質より,常に次の条件が保たれるからです.
- であり, を満たす有理数 のうちで が最小のものは である.
しかしこのアルゴリズムは最悪ケースで 時間かかります.
このアルゴリズムを,一方に加算する反復をひとまとめににします.
- である限り次を行う:
- かつ となる最大の をとり, を に取り換える.
- かつ となる最大の をとり, を に取り換える.
このような を選択するステップは 回です(常に が選ばれる場合がワーストケースで,その場合の分子・分母はフィボナッチ数列の増大度です).
本問「Rational Approximation」の場合はこのような「最大の 」が 時間で計算できるため,これで 時間の解法となります.
「最大の 」を check 関数呼び出しだけで求めることを考えましょう.例えば の範囲を二分探索するという方法が考えられますが,それではアルゴリズム全体で最悪 時間の計算量になってしまいます.
二分探索を指数探索に変えるだけで,アルゴリズム全体が 時間になります.
つまり, が足せなくなるまで をインクリメントし,足せなくなったら をデクリメントしながら貪欲に を足していきます.
に対する のインクリメントの回数, に対する のインクリメント回数をそれぞれ評価すれば,アルゴリズム全体が 時間になることは容易に分かります.