0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AtCoder Beginners Selection】初学者がPHPで解いてみた

Last updated at Posted at 2024-05-12

はじめに

AtCoder Beginners Selection をPHPで解いてみました。
これまではPythonを主に学習していましたが、エンジニアとして転職が決まり転職先でPHPを使用するため学習しています。
基本的な文法が習得できているかの確認のために行いました。

そもそも実務未経験な上、PHPは入門書を一通り学習した程度のため拙いコードだと思います。
ご指摘があれば、コメント欄で頂けると幸いです。

解答

ABC086A - Product

<?php
fscanf(STDIN, "%d %d", $a, $b);
if ($a * $b % 2 === 0) {
  echo "Even\n";
} else {
  echo "Odd\n";
}
?>

ABC081A - Placing Marbles

<?php
fscanf(STDIN, "%d", $a);
echo substr_count($a, '1'); 
?>

ABC081B - Shift only

<?php
fscanf(STDIN, '%d', $n);
$a = trim(fgets(STDIN));
$nums = explode(" ", $a);

// 可能な限り繰り返す
for ($i=0;; $i++) {
  // 要素が2で割り切れたら商で置き換え、割り切れなければ全てのforループを抜ける
  for ($j=0; $j<$a; $j++) {
    if ((int)$nums[$j] % 2 == 0) {
      $nums[$j] /= 2; 
      continue;
    } else {
        break 2;
    }
  }
}
// ループを繰り返した回数が答え
echo $i;
?>

ABC087B - Coins

<?php
fscanf(STDIN, "%d", $a);
fscanf(STDIN, "%d", $b);
fscanf(STDIN, "%d", $c);
fscanf(STDIN, "%d", $x);
$ans = 0;
// 10000円札の枚数でループ
for ($i = 0; $i <= $a; $i++) {
  // 5000円札の枚数でループ
  for ($j = 0;$j <= $b; $j++) {
    // 10000円札と5000円札の枚数から1000円札の枚数を割り出す
    $countFifty = ($x - (500 * $i + 100 * $j)) / 50;
    // 1000円札の枚数がマイナスにならず、かつC枚以下であれば答えに+1
    if ((0 <= $countFifty) && ($countFifty <= $c)) {
      $ans += 1;
    }
  }
}
print $ans;
?>

ABC083B - Some Sums

<?php
<?php
fscanf(STDIN, "%d %d %d", $n, $a, $b);
$ans = 0;
// 1〜Nまで繰り返す
for ($i = 1; $i <= $n; $i++) {
    // 各桁の数字を分割し配列にする
    $digitArray = str_split((string)$i);
    $sum = 0;
    // 配列の要素(=各桁の数字)の総和を求める
    foreach ($digitArray as $digit) {
        $sum += (int)$digit;
    }
    // 各桁の総和がA以上B以下であれば答えに加算
    if (($sum >= $a) && ($sum <= $b)) {
        $ans += $i; 
    }
}
echo $ans;
?>

はじめはstr_splitを使用せずに、数字を10で割っていき各桁の値を取得する方法で実装しようとしましたが途中で分からなくなり上記の方法になりました。
数字を10で割っていき各桁の値を取得する方法で実装している解答が以下にありました。
自分としてはこちらの数学的な解答の方が美しく感じて好きです。

ABC088B - Card Game for Two

<?php
$n = fgets(STDIN);
$a = fgets(STDIN);
$nums = explode(" ", $a);
$alice = 0;
$bob = 0;
// カードの数字を昇順に並び替え(配列の末尾が一番大きい数字になる)
sort($nums);
// アリス→ボブの順で末尾から数字を順番に取得し点数に加算
while ($nums) {
    $alice += array_pop($nums);
    if ($nums){
        $bob += array_pop($nums);
    }
}
echo $alice - $bob;
?>

ABC085B - Kagami Mochi

<?php
fscanf(STDIN, "%d", $n);
// 餅の直径の配列を用意
$d = [];
for ($i = 0; $i < $n; $i++) {
    fscanf(STDIN, "%d", $d[]);;
}
// 餅の直径を昇順に並び替える
sort($d);
// 鏡餅用の配列を用意
$kagamimoti = [];
$kagamimoti[] = array_pop($d);
while ($d) {
    // 餅の直径が鏡餅の上段より小さければ鏡餅に追加
    if (end($d) < end($kagamimoti)) {
        $kagamimoti[] = array_pop($d);
    // そうでなければ要素を削除
    } else {
        array_pop($d);
    }
}
// 鏡餅の配列の要素数が答え
echo count($kagamimoti);
?>

array_unique()関数に気づかず実装したためかなり複雑なコードになってしまいました。
array_unique()関数を使えばこんなにシンプルに書けます。

ABC085C - Otoshidama

<?php
fscanf(STDIN, "%d %d", $n, $y);
$flag = true;
// 10000円札の枚数でループ
for ($i = 0; $i <= $n; $i++) {
    // 5000円札の枚数でループ
    for ($j = 0; $j <= $n - $i; $j++) {
        // 合計金額を算出し、Y円と同じであればお札の組み合わせを出力しループを抜ける。
        $sum = $i * 10000 + $j * 5000 + ($n - $i - $j) * 1000;
        if ($sum === $y) {
            echo $i.' '.$j.' '.$n - $i - $j;
            $flag = false;
            break 2;
        }
    }
}
// お札の組み合わせが見つからない場合
if ($flag) {
    echo '-1 -1 -1';
} 
?>

ABC049C - 白昼夢

<?php
$s = trim(fgets(STDIN));
$answer = 'YES';
// 文字列Sの末尾から先頭に向かって文字をたどっていき、各単語に一致するかチェックする
while ($s) {
    if (substr($s, -5) === 'dream' || substr($s, -5) === 'erase' ) {
        $s = substr($s, 0, -5);
    } elseif (substr($s, -6) === 'eraser') {
        $s = substr($s, 0, -6);
    } elseif (substr($s, -7) === 'dreamer') {
        $s = substr($s, 0, -7);
    } else {
        $answer = 'NO';
        break;
    }
    
}
echo $answer;
?>

これは初見では無理でしたね。
以前Pythonで挑戦した時に分からず、解答例を見て文字列を末尾から逆方向に考えることは知っていたので今回は割とすんなりできました。

ABC086C - Traveling

<?php
fscanf(STDIN, "%d", $n);
// t,x,yをそれぞれ配列に格納する
$tArray = [];
$xArray = [];
$yArray = [];
$ans = 'Yes';
for ($i = 0; $i < $n; $i++) {
    fscanf(STDIN, "%d %d %d", $tArray[$i], $xArray[$i], $yArray[$i]);
}
// $ptは1つ前の時刻,$px,$pyは1つ前の座標
// $dtは1つ前の時刻と現在の時刻との差,$dx,$dyは1つ前の座標と現在の座標との差
[$pt, $px, $py] = [0, 0, 0];
for ($i = 0; $i < $n; $i++) {
    [$dt, $dx, $dy] = [abs($tArray[$i]-$pt), abs($xArray[$i]-$px), abs($yArray[$i]-$py)];
    [$pt, $px, $py] = [$tArray[$i], $xArray[$i], $yArray[$i]];
    if (abs($dx + $dy) <= $dt && $dt % 2 === abs($dx + $dy) % 2) {
        continue;
    } else {
        $ans = 'No';
        break;
    }
}
echo $ans;
?>

詳細な解説は以下の記事がとても分かりやすいです。

終わりに

新しい言語を学んだときの文法の習得度チェックにはとてもいいと思いいます。
エンジニアとして実務経験を積んだ1年後ぐらいにもう1度挑戦してコードがどのように変わるか見てみたいと思いました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?