【paiza】プログラミングでアイドルをプロデュースPHP編
前々回:【paiza】プログラミングで彼女をつくったPHP編
と引き続き、paizaさんに新しいゲーム形式プログラミング学習が登場したので、やってみました。
ということで、アイドルをプロデュースすることになります。
ゲーム内容としては、POH7の「恋愛SLG: プログラミングで彼女をつくる」と同じような
プログラミング問題を解くとアイテムのロックが解除される
方式になっています。
この方式は良かったので、この先も続けてもらいたいです。
エンディングまでは行ったので、とりあえずPHPのコードと解説などを。
なお、問題はPOH7の「恋愛SLG: プログラミングで彼女をつくる」と同じ傾向がありますので、やってると楽かも。
後ほど追加された問題については→【poh8】アイドルをプロデュースした追加問題分(PHP編その2)
ショートヘア
指定された回数分、入力された文字列を改行区切りで出力する問題です。
ノーマル版
<?php
$N = trim(fgets(STDIN));
$S = trim(fgets(STDIN));
echo str_repeat($S . "\n", $N);
?>
解説
入力された文字列を改行区切りで出力するだけなので、str_repeatに改行くっつけたものを渡すだけです。
手抜き版
<?php
$N = fgets(STDIN);
echo str_repeat(fgets(STDIN), $N);
?>
解説
trimしなかったら改行入ってくるじゃん。って手抜きするバージョンです。ま、あんまり良くないですね(笑)
ロングヘア
入力された数値が7の倍数かどうかを判定する問題です。
<?php
echo (trim(fgets(STDIN)) % 7) == 0 ? 'lucky' : 'unlucky';
?>
解説
7の倍数かどうかは、余りが0かどうかを判定します。
ポニーテール
視力検査の合格判定を判定する問題です。
ノーマル版
<?php
$a = 0;
for ($i = 0; $i < 5; $i ++) {
fscanf(STDIN, '%s %s', $d, $e);
if ($d == $e) {
$a ++;
}
}
echo $a >= 3 ? 'OK' : 'NG';
?>
解説
$dが表示、$eが回答になります。合ってる回数を数えて3以上だったらOKを出します。
配列版
<?php
$a = ['OK', 'OK', 'OK', 'NG', 'NG', 'NG'];
for ($i = count($a); $i > 1; $i --) {
fscanf(STDIN, '%s %s', $d, $e);
if ($d != $e) {
array_shift($a);
}
}
echo $a[0];
?>
解説
ノーマル版にあまりに芸がないので、固定数値をなくしてみました。
配列に、「間違えなかった場合のOK」「1回間違えたときのOK」…のように格納しておき、
間違えるたびに表示する文字列がずれていきます。
PHP配列の初期化をarray()ではなく、新しいやり方にしてみました。
paizaのは新しめのPHPなので。
ツインテール
進捗の棒を表示させる問題です。
<?php
$s = trim(fgets(STDIN));
$t = trim(fgets(STDIN));
echo str_repeat('-', $t - 1), '+', str_repeat('-', $s - $t);
?>
解説
最初のstr_repeatで進捗位置前までの-記号を出力し、次に必ずある進捗位置の記号+。その後のstr_repeatで最終位置までの-を出力します。
なんか今回str_repeat多いな。
普段は、ほとんど使いません。
たれ目
映画館の空いている席数から、座れるかどうかを判定する問題です。
<?php
fscanf(STDIN, '%d %d', $s, $n);
echo $s >= $n ? 'OK' : 'NG';
?>
解説
単純に数値の比較ですね。
つり目
購入金額からポイントカードに付くポイントを計算する問題です。
<?php
$p = trim(fgets(STDIN));
echo intval($p / 100) + ($p >= 1000 ? 10 : 0);
?>
解説
100円1ポイントなので、100で割って切り捨てします。さらに、1000円以上だと10ポイントつけます。
三項演算子を使用する場合は、括弧を適切につけないと、優先順位の関係で思いもよらないところで、判定されたりします。
今回の場合は全体を囲むようにつけます。
めがね
入力された数値を大きい順に並べ、ちょうど真ん中の数値を出力する問題です。
<?php
$N = trim(fgets(STDIN));
$a = explode(' ', trim(fgets(STDIN)));
rsort($a);
echo $a[($N - 1) / 2];
?>
解説
奇数のみの親切設計です。
explodeで文字列を配列化し、rsortで逆順に並べて、真ん中は計算で出します。
Cute衣装
飴玉がちょうど配れるかどうかという問題です。
<?php
fscanf(STDIN, '%d %d', $n, $m);
echo ($m % $n) == 0 ? 'ok' : 'ng';
?>
解説
割り切れるかどうかという話なので、余りが0かどうかですね。
Sexy衣装
進んだり戻ったりしたけど、これだけ進んだよという問題です。
<?php
fscanf(STDIN, '%d %d', $M, $N);
echo $M > $N ? $M - $N : 0;
?>
解説
進んでるのなら引きましょう。それ以外は0なのでそのように出力します。
水着
看板に足りない文字数を出力する問題です。
配列版
<?php
fgets(STDIN);
$k = array_count_values(str_split(trim(fgets(STDIN))));
$t = str_split(trim(fgets(STDIN)));
$h = 0;
foreach ($t as $u) {
if (isset($k[$u]) && $k[$u] > 0) {
$k[$u] --;
} else {
$h ++;
}
}
echo $h;
?>
解説
これはちょっと面倒ですね。
まず、入力された文字列を一文字ずつ配列にしなければ楽にならないので、str_splitを使い、1文字ずつバラバラの配列にします。
変更前の看板に、どの文字がいくつあったかは、array_count_valuesで出しておきます。
$hは再発注する小看板の数です。
変更後の看板の文字をforeachで1文字ずつループさせ、
isset($k[$u]):以前の看板にその文字があったか
$k[$u] > 0:その文字は足りているか
を判定します。
両方の条件が合致すると、以前の看板から文字数を一つ減らします。
合致しない場合、$hを増やします。
文字列版
<?php
fscanf(STDIN, '%d %d', $n, $m);
$k = trim(fgets(STDIN));
$t = trim(fgets(STDIN));
$h = 0;
for ($i = 0; $i < $m; $i ++) {
$p = strpos($k, $t[$i]);
if ($p === false) {
$h ++;
} else {
$k[$p] = ' ';
}
}
echo $h;
?>
解説
おそらく、問題としてはこちらのような回答を想定していると思われるため、こちらも載せます。
好みではないですが。
PHPは文字列を配列のように扱うことにより、1文字ずつ取り出すことができます。
なので、変更後の看板の1文字ずつを、変更前の看板にあるかどうかstrposでチェックします。
発見した場合、変更前の看板の文字を空白で潰しています。
strposと、見つからなかった場合の===falseは使用頻度は高めです。
変数削減版
<?php
fscanf(STDIN, '%d %d', $n, $m);
$k = trim(fgets(STDIN));
$t = trim(fgets(STDIN));
for ($i = 0; $i < $m; $i ++) {
$p = strpos($k, $t[$i]);
if ($p !== false) {
$k[$p] = $t[$i] = ' ';
}
}
echo strlen(str_replace(' ', '', $t));
?>
解説
基本的には文字列版と一緒ですが、変更後の看板の文字を書き換えることにより、足りない分の文字数で結果が分かるようにしています。
最後に
前回みたいに罠があるような問題はないです。
(水着はちょっと面倒かもしれませんが)
まだ公開されていない問題があるので、期待したいですね。