C – Standings

C – Standings

1 から N の番号が付いた N 人がコイントスを何回かしました。人 i は A_i 回表を出し、B_i 回裏を出したこと分かっています。

人 i のコイントスの 成功率 は A_i / (A_i + B_i) で定義されます。人 1, …, N の番号を、成功率の高い順に並び替えてください。成功率が同じ人が複数いる場合、その中では人の番号が小さい順になるように並び替えてください。

入力されるデータ
N
A_1 B_1
A_2 B_2

A_N B_N

(ただし 2 ≦ N ≦ 2×10^5
0 ≦ A_i, B_i ≦ 10^9
A_i + B_i ≦ 1 )

簡単そうで難しい問題です。
成功率を A_i / (A_i + B_i) で算出してソートすればよいのではないか?
それだと罠にかかります。

以下は成功率を A_i / (A_i + B_i) で算出してソート(成功率が同じ人が複数いる場合、その中では人の番号が小さい順にソート)しただけのコードです。

結果は不正解。

浮動小数点型による誤差

なぜか?

大小を浮動小数点型を用いて比較するのは、誤差の関係上危険です。実際、double を用いた大小比較による解法は落ちるケースが複数入っています。

2つの実数 A = 165580141 / 267914296 , B = 433494437 / 701408733 を考えてみます。実際に計算してみると、A ≒ 0.61803398874989485443509143685、B ≒ 0.61803398874989484911360520514 であり A のほうが大きいです。ところが double 型で計算すると両方とも 0.618033988749895 です。これがこの問題に仕掛けられた罠だったのです。

では、どうすればよいのか? 整数同士で比較できるようにすればよいのです。

A_1 / (A_1 + B_1) と A_2 / (A_2 + B_2) であれば双方を(A_1 + B_1) * (A_2 + B_2)倍して、A_1 * (A_2 + B_2) と A_2 * (A_1 + B_1) を比較すればよいのです。これなら誤差は入りません。

大小関係を自分で定義するには?

問題はOrderByDescendingを使ってソートする部分をどうするかです。これは以下のようにすればよいです。

まず SpecialComparer クラス(名前は何でもよいが…)を定義します。

以下のコードを提出すると AC(= 正解)となりました。