恋するハッカソン解答例と解説・制服(C++)

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

というわけで、『paizaオンラインハッカソンVol.8 恋するハッカソン〜君色に染まるアイドル〜』(以下、POH8)が始まって、ある程度の期間が経ち、当方も一通りC++のコードをGitHubに上げたが、幾つかの問題がある程度の解説が必要と考えたので、ここで順次行ってみたい。今回は制服のお題について記述する。

問題の概要

これはトランプゲームを行い、各カードを持っていて、そこから順位を割り出すというものである。カードの強さは3が最弱で、そこからK、A、2の順に強くなるというものである。

問題の難易度

今回の「制服」のお題の難易度だが、paizaランクBに相当する。これは現役で言えばそれなりのロジックを立てられてプログラミングができるスキルを持っているレベルに相当する。

回答例

サンプルコード

cf: https://github.com/saitomarch/POH8/blob/master/cpp/uniform.cpp

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <map>

using namespace std;

string readline()
{
    string str;
    getline(cin, str);
    return str;
}

vector<string> split(const string &str, char sep)
{
    vector<string> vec;
    istringstream sstream(str);
    string buf;
    while (getline(sstream, buf, sep)) {
        vec.push_back(buf);
    }
    return vec;
}

int main(void)
{
    auto strarr = split(readline(), ' ');
    vector<int> cardarr;
    for (auto str : strarr) {
        int num;
        if (str == "J") {
            num = 11;
        }else if(str == "Q") {
            num = 12;
        }else if(str == "K") {
            num = 13;
        }else if(str == "A") {
            num = 14;
        }else if(str == "2") {
            num = 15;
        }else{
            num = stoi(str);
        }
        cardarr.push_back(num);
    }

    map<int, int> rankmap;
    auto lastidx = -1;
    auto lastnum = 0;
    auto ranknum = 1;
    auto finished = false;

    while (!finished) {
        for (int idx = 0; idx < cardarr.size(); idx++) {
            if (idx == lastidx) {
                lastidx = -1;
                lastnum = 0;
                continue;
            }
            if (rankmap[idx]) {
                continue;
            }
            if (lastnum < cardarr[idx]) {
                rankmap[idx] = ranknum;
                lastidx = idx;
                lastnum = cardarr[idx];
                ranknum++;
            }
            if (rankmap.size() == cardarr.size()) {
                finished = true;
                for (auto pair : rankmap) {
                    if (pair.second == 0) {
                        finished = false;
                        break;
                    }
                }
            }
        }
    }

    for (auto pair : rankmap) {
        cout << pair.second << endl;
    }

    return 0;
}

解説

今回はstd::vectorstd::mapを使う解法を採用した。

まず、入力からカードの順番がスペース区切りでくるため、それを分割して、配列にする。その後、そのままでは使いづらいため、数値型の配列にする。この際にJ、Q、K、A、2は10よりも強いカードであるため、それを表現できるように数値を指定した。それ以外はstd::stoiで数値化した。

その後、今回はstd::mapで順位マップを作成、それを利用して順位を定義するようにした。

その上で、最後にカードを取った順番、最後に取ったカードの番号、順位を定義することによって、ループで回すようにした。

さて、今回の場合はループの処理は以下のようにしている。

  1. 最後にカードを出してから1周したら、カードをの情報をリセットして次に回す。
  2. すでにカードを出していたら次に回す
  3. もし最後に出したカードより強ければカードを出して出した人の順位を設定、その人の順番、カードの番号をセットする
  4. すべてのカードを出し切ったら終了フラグを立てる
  5. 終了フラグが立っていればループを抜ける。そうでなければ続ける。

なお、std::mapでは一度値のセットされていないキーを参照しようとすると自動的にセットされてしまうという仕様がある ((cf: std::mapの落とし穴)) ため、それを踏まえたコードにしなければならない。

なお、std::mapでは連想配列だが、キーはソートされるので、これでいける。連想配列を使わないなら、std::vectorまたは通常の配列でも使える。

最後は上記のループで割り出した順位を出力して終了である。

最後に

今回の問題はやっていることは分かりにくいが、コツさえつかめればクリア自体は難しいものではない。とはいえ配列の使い方などで難しい面はあるので、その点は気をつけたいところである。

次も解説が必要なものがあれば行っていきたい。

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