トコロテンの日記

自分の活動内容や普段考えていることをアウトプットするためのブログです。ITに関わる話がメインであり、開発、競技プログラミング、気に入った技術などの話が多くなります。

AtCoder Beginner Contest 001 C - 風力測定

問題

atcoder.jp

考察と解法

この問題は以下の2つを達成することで解決できます。

  • 風向の角度から方位を表す文字列(N, NNE, ..., NNW)への変換
  • 風速から風力への変換

変換自体は特にアルゴリズムを要求されることがなく、問題文に示されたルールに従うだけであるから簡単です。 しかし、各変換を行う際にはそれぞれ面倒くさい点があります。それは以下の2つです。

  1. 方位Nに対応する風向の角度の範囲が0.00度以上11.25未満348.75度以上360.00度未満の2つの範囲がある
  2. 風程から風速への変換、少数第2位の四捨五入で浮動小数点数の誤差が発生する場合がある

1については、方位に対する角度の範囲全てに11.25を足せばよいです。円を回すイメージです。
これによって、すべての方位に対する角度の範囲の始まりと終わりが360 / 16 = 20.5の倍数になります。
と同時に、Nに対応する角度の範囲が360.00度以上20.5未満に集約されます。
あとは角度を20.5で割った商から方位を決定できます。

2については、すべての計算を整数のみで行えばよいです。
まず、風速の範囲が少数第1位までしか存在しないため、全て10倍して整数のみにします。 風力に変換する際には、少数第2位を四捨五入したあとの風速を10倍したものが範囲に収まるか確認すればよいです。
風速の計算ですが、まず与えられた風程を予め100倍しておき、それを60(秒)で割ります。
100倍してから除算することで、失われる可能性のある少数第2位までの計算を正確に行なえます。
あとはお決まりの四捨五入テクニックで5を足した結果を10で割れば変換完了です。

変換後の風速 = (1分間の風程 * 10 / 6 + 5) / 10;

ソースコード

#include <iostream>
#include <algorithm>

using namespace std;

string degToDir(int deg)
{
    static const string dir[16] = {
        "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE",
        "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"
    };

    static const int degPerDir = 2250;

    deg = (deg * 10 + 1125) % 36000;

    return dir[deg / degPerDir];
}

int disToPower(int dis)
{
    static const int levelBounds[13] = {
        0, 3, 16, 34, 55,
        80, 108, 139, 172, 208,
        245, 285, 327
    };

    dis = (dis * 10 / 6 + 5) / 10;

    return upper_bound(levelBounds, levelBounds + 13, dis) - levelBounds - 1;
}

int main()
{
    int deg, dis;

    string dir;
    int w;
    
    cin >> deg >> dis;

    dir = degToDir(deg);
    w = disToPower(dis);

    cout << (w == 0 ? "C" : dir) << " " << disToPower(dis) << endl;
}

感想

よく考えたら風速から風力への変換時の誤差は風速の範囲の単位を風程に変換すれば消えるため、その後比較するだけでよかったです。