B - FizzBuzz Sum Editorial by TumoiYorozu


この解説は、C++ に入門したばかりの中高生レベルを想定して、考察の方法、コードの書き方の解説をします。

プログラミングの入門では FizzBuzz 問題と呼ばれる練習問題がある。

今回はそれをアレンジした問題である。

ヒント1: 入力例1を手計算で解くときの方針

入力例1では、1+2+4+7+8+11+13+14 を計算し、その答えである 60 を出力すれば良いことが分かる。

つまり、3でも5でも割り切れない数の合計を求めれば良い。

ヒント2: 繰り返し文の復習

for (int i = 0; i < N; i++) で N 回のループが書ける。

ヒント3: 割り切れるかどうか

例えば x が 3で割り切れるかどうかは x を3で割った余りが 0 かどうかで判定すると良い。

ヒント4: if の判定の書き方

ある x が「3でも5でも割り切れない」を判定するにはいくつか書き方がある。

「3で割り切れない」かつ「5で割り切れない」

if (x%3 != 0 && x%5 != 0) {
    3でも5でも割り切れないときの処理
}

ifをネスト編

if (x%3 != 0) {
    if (x%5 != 0) {
        3でも5でも割り切れないときの処理
    }
}

ifを並列編

if (x%3 == 0) {
    3で割り切れた時の処理。何も書かなくても良い
} else if (x%5 == 0) {
    3で割り切れず5で割り切れた時の処理。何も書かなくても良い
} else {
    3でも5でも割り切れないときの処理
}

ヒント5: 入力例 2 の答えが 378360980 になっちゃった
int 型は -2,147,483,648 ~ 2,147,483,647 の範囲(約±20億、\(2\times 10^9\) くらい)の整数しか扱うことができません。

入力例2の答えは 266,666,333,332 ≒ \(2.66 \times 10^{11}\) なので、間違った答えが出てしまいます。

このときは、-9,223,372,036,854,775,808~9,223,372,036,854,775,807 (約±900京、\(9\times 10^{18}\) くらい)の整数を扱える long long 型を使いましょう。

例えば以下のように使います。

#include<bits/stdc++.h>
using namespace std;
int main() {
    int N;
    cin >> N;
    long long ans = 0;
    for(int i = 0; i < N; i++){
        ans += i;
    }
    cout << ans << endl;
}

このコードは 0 以上 N 未満の整数の総和を求める事ができ、100000 と入力すると 正しく 4999950000 と計算できます。(ans を int のままで計算すると704982704になります)

これを参考に、解いてみましょう。

ヒント6: 入力例1,2は合ってるけど、WAになってしまう

例えば以下のようなコードの場合、入力例1,2は合うけど、WAになってしまいます。 実際の提出のリンクはこちら

#include<bits/stdc++.h>
using namespace std;
int main() {
    int N;
    cin >> N;
    long long ans = 0;
    for(int i = 0; i < N; i++){
        if (i%3 != 0 && i%5 != 0) {
            ans += i;
        }
    }
    cout << ans << endl;
}

プログラムの間違いを直すために、ループの中に i を表示する命令を書いて、観察してみましょう。

#include<bits/stdc++.h>
using namespace std;
int main() {
    int N;
    cin >> N;
    long long ans = 0;
    for(int i = 0; i < N; i++){
        cout << "i=" << i << endl;
        if (i%3 != 0 && i%5 != 0) {
            ans += i;
        }
    }
    cout << ans << endl;
}

このプログラムを実行すると、以下のような表示になります。

i=0
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
i=10
i=11
i=12
i=13
i=14
60

i が 15 の場合を行ってくれていませんでした。

これは、for(int i = 0; i < N; i++) と書くと、i が 0から始まって、 N 未満 までの数しかループを回してくれないからです。

次のヒントで、これを治す方法を解説します。

ヒント7: ヒント6の対処法

いくつかやり方がありますが、例えば i が 1から始まって、 N 以下にforの中身を変更する方法が一番シンプルです。

    for(int i = 1; i <= N; i++){
        cout << "i=" << i << endl;
    }

また、0以上 N+1 未満 と変更しても今回は動きます。

    for(int i = 0; i < N+1; i++){
        cout << "i=" << i << endl;
    }

しかし、i=0 は本来は行うべきじゃないので、あまり良い書き方ではありません。

他には、for の()の中身は変えずに、ループの中で新しい変数 x を用意して、 i+1 を入れてループさせる方法もあります。

    for(int i = 0; i < N; i++){
        int x = i + 1;
        cout << "x=" << x << endl;
    }

解答コード

実際の提出のリンクはこちら

#include<bits/stdc++.h>
using namespace std;
int main() {
    int N;
    cin >> N;
    long long ans = 0;
    for(int i = 1; i <= N; i++){
        if (i%3 != 0 && i%5 != 0) {
            ans += i;
        }
    }
    cout << ans << endl;
}

posted:
last update: