けんちょんの競プロ精進記録

競プロの精進記録や小ネタを書いていきます

JOI 予選 2019 A - ソーシャルゲーム (AOJ 0652) (6Q, 難易度 2)

結構頭がこんがらがる問題だと思う。

問題概要

あるソーシャルゲームでは、1 日につき 1 回までログインすることができ、ログインするたびに  A 枚のコインが得られる。

さらに、月曜日から日曜日まで 7 日連続でログインすると、そのたびに、追加で  B 枚のコインが得られる。

明日は月曜日である。 C 枚のコインを得るために必要な日数の最小値を求めよ。

制約

  •  1 \le A \le 1000
  •  0 \le B \le 1000
  •  1 \le C \le 10^{6}

解法 (1):シミュレーション

まず最もオーソドックスな方法として、日々のログインによって積み上げられるコイン総数をシミュレーションしていく方法が考えられます。

つまり、はじめて  C 枚以上となった時点でシミュレーションを打ち切る方法が考えられます。while 文などが活用できます。具体的には、次のコードのように実装できます。

コード

#include <iostream>
using namespace std;

int A, B, C;
int main() {
    cin >> A >> B >> C;
    int res = 0;
    int cur = 0;
    for (int i = 1;; ++i) {
        cur += A;
        if (i % 7 == 0) cur += B;
        if (cur >= C) {
            res = i;
            break;
        }
    }
    cout << res << endl;
}

 

解法 (2):計算で求める

シミュレーションすることなく、直接計算で求めることもできます。まず、


1 週間ごとにコインが  7A + B 枚得られる


ということに着目しましょう。そこで、 C 7A + B で割った商を  q、余りを  r とすると、次のように整理できます。

  •  q 週間ログインを続けると
  • コインの残り必要枚数が  r 枚となる

最後に、残りの  r 枚を稼ぐために必要な日数を求めましょう。つまり、1 日  A 枚ずつ積み上げて  r 枚以上となる日数を求めましょう。

これは、いわゆる切り上げ処理です。次の記事の方法によって、

(r + A - 1) / r

と求められます。ただし、これが 7 を超える場合は 7 としましょう。

drken1215.hatenablog.com

以上の解法をまとめると、次のように実装できます。

コード

#include <bits/stdc++.h>
using namespace std;

int main() {
    int A, B, C;
    cin >> A >> B >> C;

    int coins_per_week = A * 7 + B;  // 1 週間あたりのコイン枚数
    int weeks = C / coins_per_week;  // 必要週数
    int nokori_coins = C % coins_per_week;  
    int nokori_days = min((nokori_coins + A - 1) / A, 7);  // 切り上げ処理
    cout << weeks * 7 + nokori_days << endl;
}