Help us understand the problem. What is going on with this article?

[PHP基礎体力向上]競プロ(AOJ-ICPC)の基礎問題をPHPで解いていこう!

この記事の対象読者

  • paizaでランクアップを目指す(Aランクくらいまで)の未経験/駆け出しエンジニア
  • ドットインストールや入門書籍でPHPを学んだが、まだ”手に馴染んでいない”初学者

この記事を書いた目的

書籍やドットインストール等のオンライン学習教材でプログラミング言語を学んだ初学者がいきなりWebサイトを作るのは中々ハードルが高いです。

そういった初学者にオススメしたいのが簡単なプログラミング問題を解いて練習することです。
この練習を通して「サービスの企画に力を分散させることなく、純粋にコーディング能力を鍛える」ことが出来ます。

これは後々Webサービスを開発する上でも絶対に役立つ基礎力になるはずです。

これは未経験からソフトウェア開発職にキャリアチェンジしたい方にもオススメのやり方です。
例えばpaizaというコーディングテストを受けて就職活動が出来るサービスではAランクくらいまで取れればかなりの数の企業に”エントリーシートなしで”面接することが出来ます。またpaiza以外の媒体で面接した企業の方にも「paizaでAランクある」と伝えると中々好評でした。

ただ、paizaの問題は基本的に外部で解説等することが禁止されているので今回はAOJ-ICPCというサイトを使って一番基礎的なレベル100の問題(一番下といってもpaizaでBランククラスはあるかと思います。)を丁寧に解説しながら解いていこうと思います!

※まぁ結論すごく面白いしコーディングの筋力アップに繋がるしついでに(paizaでランクも取れて)就職でも有利になるのでワイとAOJ-ICPCやろうズ!

下準備

  • VSCode編
    PHPのプラグイン入れる
    code-runner入れる
    ->執筆予定!

  • Vim編
    Vimの練習も同時にしたいんだ!というツワモノはこちらを参考にしてください。

「Vimでのpaiza下準備をステップバイステップで丁寧に」

問題を理解する

今回取りあげるのはICPC 得点集計ソフトウェアという問題です。

まず問題を解く前に(特に初学者のみなさんは)問題文を音読で読んでしっかり理解するようにしてください。

読めましたか?余裕がある方は下を読んでいく前に自力で解いていきましょう。(その際はメモ帳片手にどういうロジックで解いていくか考えながらやってみましょう!)

まずどういうデータが与えられるのかを考える

  • 審査員の人数nが与えられる。(3 <= n <= 10)
  • その後に審査員の数だけ点数sが与えられる。(0 <= s <= 1000)
  • 入力の終わりには審査員の数に0が与えられる。

以上から下記のような形式でデータが与えられるとわかります。

//inputは下記のような形式で与えられる。
審査員の人数n
s_1
s_2
s_3
.
.
.
s_n-1
s_n
審査員の人数m
s_1
s_2
.
.
.
s_m
0 ->ここで入力おわり!
  • どういう答えを出すべきか

演技の点数は審査員の出した点数の合計から最大値と最小値を引く。
その後、審査員の数から最大値と最小値を出した審査員分だけ(2)引く数で割る。
点数は『平均点には端数があるかもしれないが,それは切り捨てて最終的な点数は整数値』とすること。

まずはベタ書きの処理をコーディングしてみる

  • 審査員の人数を取得

標準入力を取得するにはfgets(STDIN)。
標準有力はstring型(文字)として取得されるので型キャストする。(int)でint型になる。

$judge_num = (int) fgets(STDIN);
  • 審査員の数だけスコアを取得、それを配列に入れていきます。
$score_list = [];
for($i=0;$i<$judge_num;$i++){
    $score_list[] = (int)fgets(STDIN);
}
  • 最大値と最小値を除いた平均(調整平均)を計算

配列の合計、最大値、最小値を取得するにはarray_sum関数max関数min関数
平均の値は小数点を切り捨てするように、と問題文にあるのでfloor関数を使用しましょう。

$max_num = max($score_list);
$min_num = min($score_list);
$score_sum = array_sum($score_list) - $max_num - $min_num;
$adjusted_ave_score = floor($score_sum / ($judge_num - 2));

これで一回の審査分の点数を算出できます。

ただ、今回は審査員数0となるまで点数を出すようにしたいです。
何回処理をするかがわからない。。。そんなときはforループではなくwhile文を使いましょう。

while(True){
  $judge_num = (int) fgets(STDIN);
  //審査員の数が0のときwhileループがbreakされる。
  if ($judge_num === 0){
      break;
      }
      //処理
      .
      .
      .
  }

以下が完成品です。

<?php
while(True){
        //審査員の数を取得
        $judge_num = (int) fgets(STDIN);
        if ($judge_num === 0){
                break;
        }
        //スコアを配列に入れていく
        $score_list = [];
        for($i=0;$i<$judge_num;$i++){
                $score_list[] = (int)fgets(STDIN);
        }
        //最大値と最小値を取得
        $max_num = max($score_list);
        $min_num = min($score_list);
        //調整平均を算出
        $score_sum = array_sum($score_list) - $max_num - $min_num;
        $adjusted_ave_score = floor($score_sum / ($judge_num - 2));
        echo $adjusted_ave_score . PHP_EOL;
}

関数化してみる

関数とはPHP本格入門 [上]/大家正登 著によれば、『人間にとって意味を持つ一連のプログラム処理をまとめ、オリジナルな名前を持つ一つの命令として扱えるようにする』ものです。

関数を使うメリットは、

(1)DRYなコードになる(コピペで何箇所にもプログラム処理を書く必要がなくなる)
(2)可読性の向上(後で読み返した時に関数の名前付けやコメントをしっかり書いておけば処理の中身を読み込む手間なく何をしているのかわかる)

の2つです。

今回はいきなり完成品を示します。

<?php

function get_input_num(){
        $input_num = (int) fgets(STDIN);
        return $input_num;
}

function make_score_list($judge_num){
        $score_list = [];
        for($i=0;$i<$judge_num;$i++){
                $score_list[] = get_input_num();
        }
        return $score_list;
}

function calculate_adjusted_ave_score($judge_num, $score_list){
        $max_num = max($score_list);
        //echo $max_num;
        $min_num = min($score_list);
        //echo $min_num;
        $score_sum = array_sum($score_list) - $max_num - $min_num;
        $adjusted_ave_score = $score_sum / ($judge_num - 2);
        return $adjusted_ave_score;
}

while(True){
        //審査員の人数を取得
        $judge_num = get_input_num();
        //審査員の数値が0のとき入力終わり、whileから抜け出す
        if ($judge_num === 0){
                break;
        }
        //echoで数値を確認しながらコーディングしていこう!必要なくなったらコメントアウト
        //echo $judge_num;

        //スコアを配列に入れてやる
        $score_list = make_score_list($judge_num);
        //配列はvar_exportで確認してみよう!
        //var_export($score_list);

        //最大値と最小値を除いた調整平均を出す
        $adjusted_ave_score = calculate_adjusted_ave_score($judge_num, $score_list);
        //小数点は切り捨て
        $adjusted_ave_score = floor($adjusted_ave_score);
        echo $adjusted_ave_score . PHP_EOL;
}

自分で書いてみよう!(他のロジックを考える)

たとえばスコアを配列で取得してmax、min関数を使うというロジックから。。。

max_num = -1
min_num = 1001

と設定してこれらの変数とスコアを標準入力から取得するたびに比較して更新していくなど。

他にどんなロジックを使えば書けるのか考えていきましょう!

digitalhimiko
重電メーカーで二年間営業してたけど、Web開発エンジニアになりました。vim幼稚園児。もっと強くなりたい。 PHP(業務)とC/C++(趣味)を行ったり来たり。なにかあればTwitterまでどうぞ!
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした