AGC019のAでコケました。
問題としては、
0.25リットルのジュースはQ円、
0.5リットルのジュースはH円、
1リットルのジュースはS円、
2リットルのジュースはD円。
ぴったりNリットルのジュースが欲しい場合、
一番安い組み合わせで買ったらいくらになりますか?(意訳)
ただし、Q、H、S、D、Nはすべて整数とする。
というもの。
解法としては、Nは整数なので、1リットルで一番安いものを計算します。具体的には
LITTER_1 = min(Q * 4, H * 2, S)
こんな感じ。
で、後は、2リットルと1リットルの単価を比較して計算する、という流れなんですけれども。
その中で、なぜか通らないコードの一部を抜粋したのが、以下です。
<?php
$litter1 = 99999995;
$litter2 = 99999997;
$question = 999999999;
// 1リッターが2つの方が高くつく場合
if ($litter1 * 2 > $litter2) {
// 問題が偶数の場合
if ($question % 2 == 0) {
printf("%d\n", $question / 2 * $litter2);
} else {
printf("%d\n", floor($question / 2) * $litter2 + $litter1);
}
} else {
printf("%d\n", $question * $litter1);
}
正解は 49999998499999998 なんですが……
実行してみると、なんか 6 ずれてるんです。
なんでだろう、と思って、細かく見てみて、唖然。
$step1 = floor($question / 2);
printf("[1]%d\n", $step1);
printf("[2]%s\n", gettype($step1));
$step2 = $step1 * $litter2;
printf("[3]%d\n", $step2);
printf("%d\n", floor($question / 2) * $litter2 + $litter1);
999999999 を 2 で割って、床関数で切り捨てて、 499999999 を得ています。[1]
これはOK。
そこに 99999997 を掛けて…… **[3]**を見ると、明らかにおかしい。499999999 × 99999997 で最後にこんなに 0 が並ぶことは、ありえない。
[1]の時点での変数の型を調べてみると、doubleになってます。[2]
この時点で、何かちょっとおかしい。床関数を使って切り捨てている。整数を得ているはずなのに、なぜ double なのか。
マニュアルを見ると、
value をこえない最大の整数の値を (float 型で) 返します。
と書いてあります。
なぜfloat型なのか。
これは、 float の範囲が int よりも広いためです。
……???
ちょっと何を言ってるのか分かりませんが、浮動小数点の形で返されているせいで、正しい数を得ることができていません。
(int)
でキャストしても良いですが、とりあえず整数型で返す関数を定義します。
function ifloor($value) {
return intval($value);
}
こうすることによって、正しい値を得ることができました。
結論
floor
やceil
を使うときは、いちいちintにキャストした方が良いよ。
いやぁ、こんな罠が潜んでいるとは……