【poh8】アイドルをプロデュースした追加問題分(PHP編その2)
前回の
【paiza】プログラミングでアイドルをプロデュースPHP編
の後に、予定通り問題が追加されましたので、やってみます。
今後追加分はこのページをアップデートして記載していく予定です。
現在は6/22の浴衣分までです。
マイク
イベントを何週でこなせるかという問題です。
<?php
$n = trim(fgets(STDIN));
$m = trim(fgets(STDIN));
echo ceil($m / ($n * 2));
?>
解説
イベント回数を1日でこなせる回数×2で割ります。
最終的な単位は週なので、小数点はありえません。よって切り上げのceilを使います。
カチューシャ
サイン会で用意するペンと色紙の値段を出す問題です。
<?php
fscanf(STDIN, '%d %d', $n, $p);
fscanf(STDIN, '%d %d', $m, $q);
echo $n * $p + ceil($n / $m) * $q;
?>
解説
色紙の値段は、人数$nと色紙枚数$pを単純に書けた値段です。
ペンは1本のペンで書ける枚数が決まっているため、色紙の枚数から割り出した分用意します。
ただし、ペンの本数も小数点以下はなく、1本単位なので、切り上げのceilを使います。
制服
トランプを一枚ずつ配り、大富豪みたいなゲームをして順番を出力する問題です。
読んだまま作った暫定版
<?php
$t = array_flip(['3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A', '2']);
$n = $p = explode(' ', trim(fgets(STDIN)));
foreach ($p as &$z) {
$z = $t[$z];
}
$m = count($p);
$c = -1;
for ($j = 1; $j <= $m; ) {
for ($i = 0; $i < $m; $i ++) {
if ($p[$i] != -1 && ($c == -1 || $c < $p[$i])) {
$c = $p[$i];
$p[$i] = -1;
$n[$i] = $j;
$j ++;
if (max($p) <= $c) {
$c = -1;
}
}
}
}
echo implode("\n", $n);
?>
解説
とりあえず、読んだまま作ったコードです。
2行目:カードの文字で配列を作り、それをarray_flipでキーと値を入れ替えます。これにより、キーにカードの文字、値に0~12が入る連想配列を作ります。
3行目:explodeでカードが配られた状態を配列にします。$pはプレイヤーのカード。$nは順位を格納する領域です。
4行目:foreachでカードの文字→強さに変換しています。
9行目:二重ループの外側は、順位です。配られた枚数まで順位を決めます。
10行目:内側のループはカードをサーチしています。
11行目~15行目:$cは場に出されているカードです。-1は何も出されていない状態です。
$p[$i]は$iさんが持っているカードの強さです。場に出すと-1になります。$cと$p[$i]を比較することにより、場に出せるかどうかを決めます。
$n[$i]は$iさんの順位を入れます。
16行目:max($p) <= $cはカードが強すぎて誰も出せないよね?というのを調べてます。
22行目:終わったころには$pはすべて-1に$nは順番で埋められていますので、改行区切りで出力します。
コードでトランプしました(笑)
再帰版
<?php
function srh($x)
{
global $c, $p, $i, $m;
if ($x == $i) {
$c = -1;
} else {
$x = ($x == $m) ? 0 : $x;
if ($p[$x] > $c) {
return $x;
}
}
return srh($x + 1);
}
$n = $p = explode(' ', trim(fgets(STDIN)));
foreach ($p as &$z) {
$z = strpos('345678910JQKA2', $z);
}
$m = count($p);
$c = $i = -1;
for ($j = 1; $j <= $m; $j ++) {
$i = srh($i + 1);
$c = $p[$i];
$p[$i] = -1;
$n[$i] = $j;
}
echo implode("\n", $n);
?>
解説
再帰関数で作ったら分かりやすくなるよねと思って作ったコードです。
srh関数は後ほど解説するとして。
16行目:explodeでカードが配られた状態を配列にします。$pはプレイヤーのカード。$nは順位を格納する領域です。
17行目:foreachでカードの文字→強さに変換しています。分かりやすいようにstrposを使ってみました。10のところで一個飛びますが、比較だけなので問題ないでしょう。
22行目:ループは順位です。配られた枚数まで順位を決めます。
23行目:srh関数で次にカードを出せる人$iさんをサーチします。
[sth関数]
5行目:一周したかどうかを判定しています。一周した場合、6行目でカードをリセット($cを-1にする)にして二週目に突入します。
8行目:端っこまでいったら0からやり直します。
9行目:カードが出せるかどうかで、出せたらreturnします。
13行目:次の人へ…
6/22 New!
おさげ
CDに曲が入るかどうかを出力する問題です。
スタンダード版
<?php
$n = trim(fgets(STDIN)) * 60;
$m = trim(fgets(STDIN));
for ($i = 1; $i <= $m; $i ++) {
$n -= trim(fgets(STDIN));
$o = $n >= 0 ? $i : $o;
}
echo $n >= 0 ? 'OK' : $o;
?>
解説
問題自体は簡単ですので、いかに効率よく作るかという感じですね。
$iをそのまま曲順にしておいて、最後まで取っておく手法です。
再帰関数版
<?php
function rec($i, $o, $n, $m)
{
$n -= trim(fgets(STDIN));
return $m == $i ? ($n < 0 ? $o : 'OK') : rec($i + 1, $n < 0 ? $o : $o + 1, $n, $m);
}
echo rec(1, 0, trim(fgets(STDIN)) * 60, trim(fgets(STDIN)));
?>
解説
$mを引数に渡す意味はないです。
半分ネタです。
浴衣
冷蔵庫の電気料金を計算する問題です。
isset版
<?php
for ($i = trim(fgets(STDIN)); $i > 0; $i --) {
fscanf(STDIN, '%d %s', $t, $s);
$d[$t] = $s == 'in' ? 5 : 3;
}
$t = isset($d[0]) ? $d[0] : 0;
$e = 0;
for ($i = 1; $i < 25; $i ++) {
if ($t > 0) {
$e ++;
$t --;
}
$t += isset($d[$i]) ? $d[$i] : 0;
}
echo 24 + $e;
?>
解説
これも問題自体は簡単で、いかに効率よく作るかという感じになります。
引っかかりやすいのは、6行目の温度の初期値を入れるかどうかが変わるところでしょうか。
0時に扉を開け閉めするかもしれないので。
さらに24時まで計算するので、開け閉めの値と最後が1時間ずれます。
4行目で最初から何度プラスになるかを入れておいて、issetでその時間に温度がプラスになるかどうかをチェックする手法です。
$tが現在の温度。$eに電気代の標準値より多い分を設定しておきます。
どのみち24時間で電気代24円は必要なので、最後のechoで足しておきます。
配列使いまわし版
<?php
$d = array_fill(0, 24, 0);
for ($i = trim(fgets(STDIN)); $i > 0; $i --) {
fscanf(STDIN, '%d %s', $t, $s);
$d[$t] = $s == 'in' ? 5 : 3;
}
$t = 0;
foreach ($d as &$o) {
$t += $o + ($t > 0 ? -1 : 0);
$o = $t > 0 ? 2 : 1;
}
echo array_sum($d);
?>
解説
isset版と違い、最初に配列を作ります。
そうすることにより、issetが不要となります。
せっかく配列を作ったので、ループにも使います。ループに使うとisset版と異なり、未来に起こる温度の変化と電気料金を計算する事になります。
そうまでしてループに使ったので、配列の中身を電気料金に書き換えておきます。
そうすると、最後に合計すると電気料金になる仕掛けです。
最後に
以後問題追加待ちです。