PaizaオンラインハッカソンVol.6に参加してみた(六村リオミッション, C++)

注意: この記事は1年以上前に掲載されたものです。情報が古い場合がありますのでお気を付け下さい。

PaizaオンラインハッカソンVol.6が開始されたにて、PaizaオンラインハッカソンVol.6「女子高生プログラマーの大バトル! ~コボール文明の逆襲~」が開始したという記事を書いたが、今回は六村リオミッションを選択して、実際に書いてみた。いつものことながら、今回はC++で書いてみた。

回答例

#include <iostream>
#include <vector>
#include <sstream>

using namespace std;

/// 文字列を分割する
/// @param str 文字列の参照
/// @param sep 分割に使う文字
/// @return 分割後の配列(std::vector)
vector<string> splitStringToVector(const string &str, char sep) {
    vector<string> vec;
    stringstream sstream(str);
    string buf;
    while (getline(sstream, buf, sep)) {
        vec.push_back(buf);
    }
    return vec;
}

/// コーヒーを定義する
class Coffee {
    private:
    /// コーヒーの粉の量
    float _powder;

    /// 湯(水)の量
    float _water;

    public:
    /// コンストラクタ
    Coffee() : _powder(0), _water(0) {}

    /// 合計を取得する
    /// @return 粉と水の合計値
    float getTotal() {
        return _powder + _water;
    }

    /// コーヒー粉を入れる
  /// @param powder 粉の量
    void addPowder(float powder) {
        _powder += powder;
    }

    /// 湯(水)を入れる
    /// @param water 湯(水)の量
    void addWater(float water) {
        _water += water;
    }

    /// 濃度を取得する
    /// @return 濃度
    template <typename T> T getConsentration() {
        return static_cast<T>((_powder / getTotal()) * 100.0);
    }

    /// 味見をする
    /// @param quantity 味見をするコーヒーの量
    void taste(float quantity) {
        auto total = getTotal();
        _powder -= quantity * _powder / total;
        _water -= quantity * _water / total;
    }
};

int main(void){
    // 行数を取得する
    string linesString;
    getline(cin, linesString);
    int lines = stoi(linesString);

    Coffee coffee;

    for (auto line = 0; line < lines; line++) {
        // カラムのタイプを取得する
        enum {
            ACT_TYPE = 0,
            QUANTITY,
        };
        // 行動のタイプを定義する
        enum {
            ADD_WATER = 1,
            ADD_POWDER = 2,
            TASTE = 3,
        };

        string actString;
        getline(cin, actString);

        auto actArr = splitStringToVector(actString, ' ');

        int action = stoi(actArr[ACT_TYPE]);
        float quantity = stof(actArr[QUANTITY]);
        switch (action) {
        case ADD_WATER:
            coffee.addWater(quantity);
            break;
        case ADD_POWDER:
            coffee.addPowder(quantity);
            break;
        case TASTE:
            coffee.taste(quantity);
            break;
        default:
            throw "Invalid action type detected.";
            break;
        }
    }
    cout << coffee.getConsentration<int>() << endl;
    return 0;
}

解説

準備事項

今回のミッションをC++で行う場合、最低限クラスを使ったオブジェクト指向プログラミング ((今回はコーヒーをクラス化)) 、できればテンプレートを使ったジェネリックプログラミング ((今回は濃度の取得に使用した)) は覚えておくとよいだろう。なお、今回もSTLを有効に使うことによってかなり簡単にコードが書けるので覚えておくとよいだろう。

文字列の分割

まず、splitStringToVectorだが、これは特定の文字列を任意の文字を使って分割するという処理だが、ここではスペースが分割に使う文字になっている。Paiza系のプログラムチェックでは文字列の分割は必ずと言っていいほど使われるので、この処理はC++で使うにはぜひ使いまわすと良いだろう。

なお、文字列の分割のためにiostreamの他に、vector、sstreamのインクルードが必要である。

Coffeeクラスの定義

次にコーヒーというクラスだが、これは粉を示す_powderと湯(水)を示す_waterをprivate変数とし、あとは「湯(水)を入れる」、「粉を入れる」、「合計を取得する」、「濃さを取得する」、「味見をする」という処理を定義した。

なお、「濃さを取得する」については、今回はパーセンテージにするためにメソッド内で100掛け、また浮動小数点数を使う場合を想定してテンプレートを使った。ただ、今回の場合はメソッド内で強制的に100掛けをするようにしてしまった点は失敗だった。最低限100掛けするかどうかを引数に追加すればよかったと考えている。

メイン処理

今回はメイン処理は以下の段階を経ている

  1. 行数の取得
  2. コーヒーのオブジェクトを生成する
  3. コーヒーの粉あるいは湯を入れる、あるいは味見するの処理を行数分繰り返す
  4. 濃さを出力する

入力の1行目はそのあと何回の処理が行われるかを定義している。C++では文字列の長さを取得して文字列の長さが0であれば処理を打ち切るという方法でも行えるが、今回は1行目の数値分forループで回すという方法を行った。

その後、コーヒーオブジェクトを生成して、入力の1行目で指定された分処理を行う。今回は2カラム構成で、左が処理のタイプ(1:湯を入れる、2:粉を入れる、3:味見する)が、右が数量と定義されているため、それに従って処理を行った。

なお、基本的には左には1、2、3以外は入らないため、万一1〜3以外があった場合は例外を投げるようにしている。

一連のコーヒーの処理が終わったら、最後に濃さを整数型で出力して終わりである。

結果

cf: https://paiza.jp/poh/joshibato/rio/result/6803bdc6

上記のコードで、すべてのテストケースで通過、なおかつ処理時間は0.01秒だった。

最後に

今回のPOH Vol.6の六村リオミッションについては、最低限オブジェクト指向の考え方が理解していて、問題文の意味がわかっていれば簡単にクリアできるので、おちついて問題をこなしてみよう。

次はまた別のミッションの結果を書いてみたい。

タイトルとURLをコピーしました