B - Achieve the Goal Editorial by TumoiYorozu


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

まず入力例1に注目して、解き方の方針を考えてみよう。 入力例1は、要するに

  • 5教科のテストを受ける
  • 今までのテストの成績は、\(8, 10, 3, 6\) 点であった。
  • 平均点を7点以上にしたい。最後のテストでは何点取る必要があるだろうか?

というケースである。プログラミングのことは一旦忘れて、数学のテストで上の様な問題が出されたら、どう求めるだろうか?

いくつかのヒントに分けて書きましたが、ヒントを読んで分かりそうになったら自力で解けるかまた挑戦しましょう!

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

平均点を7点にしたいということは、5教科合計で7×5=35点を取るというのが目標である。

ヒント2: 入力例1を手計算で解くときの立式

今までの4教科で、8+10+3+6=27点取れた。 5教科合計で7×5=35点取りたいので、最後のテストでは35-27 = 8 点取れば良い。

ヒント3: プログラムで解く時の方針

今までに取った合計点を計算して、目標の合計点からいままで取ったテストの合計点数を引くと、最後のテストで取るべき点数である。

ヒント4: テストの点数 A をループで受け取る

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

今回は N-1 回のループなので、これを少しいじる必要がある。

ヒント5: テストの点数 A をループで受け取る(具体的に編)

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

以下のように書けば、N-1 個の a を受け取れる。

#include <bits/stdc++.h>
using namespace std;
int main() {
    int N, K, M;
    cin >> N >> K >> M;
    ~~処理①~~
    for(int i = 0; i < N-1; i++) {
        int a;
        cin >> a;
        ~~処理②~~
    }
    ~~処理③~~
}

N-1個の a が受け取れたら、ヒント3で考えた目標の点数を計算する処理を書こう!

ヒント6: 目標の点数を計算する(方針)

ヒント5内のコードの

  • 処理① に目標達成に必要な合計点を入れる変数を作ろう!
  • 処理② にそれぞれの点数を合計する処理を書こう!
  • 処理③ に最後に取るべき点数を求めて、答えを出力しよう!

ヒント7: 今まで取ったテストの合計点を求める

処理① に合計点数を記録する変数 sum を作る。0で初期化するのを忘れずに。 処理② では、sum = sum + a と書くことでsumに合計する事ができる。sum += a と省略する書き方もある。

#include <bits/stdc++.h>
using namespace std;
int main() {
    int N, K, M;
    cin >> N >> K >> M;

    int sum = 0;
    for(int i = 0; i < N-1; i++) {
        int a;
        cin >> a;
        sum += a;
    }
    int x = 【目標点を計算する式を入れる】
    ~~処理③~~
}

最後にヒント3 を参考にして目標点 \(x\) を求めて、答えを表示しよう。

ヒント8: 目標点を計算する。

平均点を科目数で掛けると合計の目標点が得られる。それから今まで獲得したテストの点数を引けば、最後のテストで取るべき点数がわかる。

よって

int x = N * M - sum;
cout << x << endl;

と書けば、入力例 1を試すと 8 という答えが返ってくる。

しかしこれでは処理③は不完全である。 入力例 2, 3 も試してみよう。そしてどうすれば良いか考えてみよう。あともう少しである。

ヒント9: 入力例2のコーナーケース

ヒント8 までのコードを試すと、入力例2は-60が出力されるが、正解は0である。

この-60とはどういう意味かを考えると、「最後のテストでは-60点以上取れば良い」を意味する。 しかし、テストの点数は0点以上なので、答えとしては「0点以上取れば良い」と答えないといけないのである。

ヒント9.1: 入力例2のコーナーケースの対処方法

目標点数が0未満になってしまったら、0を出力すれば良い。

ヒント10: 入力例3のコーナーケース

ヒント9 までのコードを試すと、入力例3は240が出力されるが、正解は-1である。

これは、テストはK点満点であるから、240点は取れない。問題文には『達成不可能である場合は、代わりに -1 を出力してください。』と書かれているので、今回は-1が答えである。

ヒント10.1: 入力例3のコーナーケースの対処方法

目標点数がKより大きくなってしまったら、-1を出力すれば良い。 入力例2の様な0以下のケースも考慮すると、以下のように書くと良い。

if (x < 0) {
    cout << 0 << endl;
} else {
    if (x > K) {
        cout << -1 << endl;
    } else {
        cout << x << endl;
    }
}

2行目は、目標点xが0未満のケースである。

xが0以上であれば3~9行目のelse パートが実行される。

その中で、またKと比較するifを書くことが出来る。

5 行目は x が K より大きいときに実行され、7 行目はそうじゃないときに実行される。

また以下のように書くと、よりスッキリする

if (x < 0) {
    cout << 0 << endl;
} else if (x > K) {
    cout << -1 << endl;
} else {
    cout << x << endl;
}

解答コード

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

#include <bits/stdc++.h>
using namespace std;
int main() {
    int N, K, M;
    cin >> N >> K >> M;

    int sum = 0;
    for(int i = 0; i < N-1; i++) {
        int a;
        cin >> a;
        sum += a;
    }
    int x = N * M - sum;
    if (x < 0) {
        cout << 0 << endl;
    } else if (x > K) {
        cout << -1 << endl;
    } else {
        cout << x << endl;
    }
}

少し違う書き方

目標達成に必要な合計点 \(x\) という変数(箱)を最初に用意して、各テストを受けるごとに \(x\) からテストの点数を引くと、最後に \(x\) に残った点数が、最後のテストで取るべき点数である。

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

#include <bits/stdc++.h>
using namespace std;
int main() {
    int N, K, M;
    cin >> N >> K >> M;

    int x = N * M;
    for(int i = 0; i < N-1; i++) {
        int a;
        cin >> a;
        x -= a;
    }
    if (x < 0) {
        cout << 0 << endl;
    } else if (x > K) {
        cout << -1 << endl;
    } else {
        cout << x << endl;
    }
}

posted:
last update: