ファンクラブサイトはこちら
AtCoder

【C++解説】AtCoder Beginner Contest 001-B – 視程の通報【競技プログラミング】

mintson

本日の問題

今回はAtCoder Beginner Contest 001のB問題視程の通報を解いていきます。

※YouTubeに解説動画あります。

問題文を要約すると、視程(肉眼で物体がはっきりと確認できる最大の距離)をメートルで受け取り、以下のルールに従ってVVという2桁の整数に変換して出力するプログラムを作る問題です。

  1. 0.1km 未満:VVの値は 00 とする。
  2. 0.1km 以上 5km 以下:距離(km)を10倍した値とする。1桁の場合は上位に 0 を付す。
    • 例:2,000m = 2.0km → VVは 20。200m = 0.2km → VVは 02
  3. 6km 以上 30km 以下:距離(km)に50を足した値とする。
    • 例:15,000m = 15km → VVは 65
  4. 35km 以上 70km 以下:距離(km)から30を引いて5で割った後、80を足した値とする。
    • 例:40,000m = 40km → VVは 82
  5. 70km より大きい:VVの値は 89 とする。

入力は距離 m(0 ≦ m ≦ 100,000)が1行で与えられ、計算結果が整数にならない入力や、上記範囲に入らない入力(例:5kmより大きく6km未満)はないことが保証されています。

https://atcoder.jp/contests/abc001/tasks/abc001_2

解説

一番オーソドックスなif文で条件を全て書き出すと以下のようになります。

#include <iostream>
#include <iomanip>
using namespace std;

int main() {
    int m;
    cin >> m;

    int vv;
    if (m < 100) {                 // 0.1km 未満
        vv = 0;
    } else if (m <= 5000) {        // 0.1km 以上 5km 以下: km×10 = m/100
        vv = m / 100;
    } else if (m <= 30000) {       // 6km 以上 30km 以下: km+50
        vv = m / 1000 + 50;
    } else if (m <= 70000) {       // 35km 以上 70km 以下: (km-30)/5+80
        vv = (m / 1000 - 30) / 5 + 80;
    } else {                       // 70km より大きい
        vv = 89;
    }

    cout << setw(2) << setfill('0') << vv << endl;
    return 0;
}

もう一つの書き方として、三項演算子(?:)を使って条件分岐をスッキリ書くことができます。

三項演算子(?:)の基本

三項演算子は「条件式 ? 真の場合の値 : 偽の場合の値」という形をとります。if-else文を1行で書けるコンパクトな書き方です。

条件式 ? 真の場合の値 : 偽の場合の値

// 基本形
条件式 ? 真の場合の値 : 偽の場合の値

// 例
int x = (a < b) ? a : b; 

今回の条件で三項演算子を使ってみると以下のようになります

int vv =
        m < 100 ? 0 :
        m <= 5000 ? m / 100 :
        m <= 30000 ? m / 1000 + 50 :
        m <= 70000 ? (m / 1000 - 30) / 5 + 80 : 89;
  1. m < 100 → true → 0を代入して終了
  2. 2.ダメなら m <= 5000 → true → m / 100を代入して終了
  3. 3.ダメなら m <= 30000 → true → m / 1000 + 50を代入して終了
  4. 4.ダメなら m <= 70000 → true → (m / 1000 - 30) / 5 + 80を代入して終了
  5. 5.全部ダメなら 89を代入

三項演算子は簡潔に書けますが、条件を間違えやすい欠点もあります。
慣れないうちはif-elseで書き、コードに慣れてきたら三項演算子も使ってみるのがお勧めです。

最後に今回の問題の出力について解説します。

cout << setw(2) << setfill('0') << vv << endl;

この1行で「VVの値を2桁のゼロ埋め付きで出力し、改行する」という処理を行っています。問題文で「VVは必ず(上位の 0 を含めて)2桁の整数」と指定されているため、02 のように1桁の値には先頭に 0 を付ける必要があります。

各部分の役割

setwsetfill を使うには <iomanip> のインクルードが必要です。

#include <iomanip>

※iomanip は io(I/O=入出力)+ manip(manipulator=操作子)の組み合わせで、「入出力を操作するためのヘッダ」という意味です。

1行ずつ分解すると以下のようになります。

部分役割詳細
cout標準出力画面への出力を行う
setw(2)出力幅を2に指定次に出力する値を最低2桁で表示する(※set width)
setfill('0')ゼロ埋め桁が足りない部分を 0 で埋める

setw(2) だけだと空白埋め( 2)になってしまうので、setfill('0') と組み合わせてゼロ埋め(02)にしています。

ショート動画

ABOUT ME
夜猫ミント
夜猫ミント
クリエイター
2020年からネット活動をしているものです。
記事URLをコピーしました