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

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

JOI 一次予選 2024 (第 2 回) A - 飴の袋詰め (9Q, 難易度 1)

例によって、一次予選の A 問題は教育的!

問題概要

1 個  A 円の飴を  B 個と、 C 円の袋を 1 つ買う。

合計金額はいくらか求めよ。

解法

答えは  AB + C です。

プログラムでは、整数値 A, B, C を標準入力で受け取って、整数値 A * B + C を標準出力で出力すればよいでしょう。

コード

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

int main() {
    int A, B, C;
    cin >> A >> B >> C;
    cout << A * B + C << endl;
}

JOI 一次予選 2024 (第 1 回) D - 現れている数字 (6Q, 難易度 2)

とても教育的な問題ですね。

問題概要

0 以上 9 以下の整数からなる、 N 個の整数  A_{1}, A_{2}, \dots, A_{N} が与えられる。

数列  A の中に一度以上登場する整数を小さい順に出力せよ。

 

解法 (1):値 0, 1, ..., 9 について個別に全探索

1 つめの解法は

  • まず、値 0 が数列に含まれているかどうかを判定し、含まれていれば 0 を出力する
  • 次に、値 1 が数列に含まれているかどうかを判定し、含まれていれば 1 を出力する
  • 次に、値 2 が数列に含まれているかどうかを判定し、含まれていれば 2 を出力する
  • ...
  • 最後に、値 9 が数列に含まれているかどうかを判定し、含まれていれば 9 を出力する

という方法だ。

コード

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

int main() {
    int N;
    cin >> N;
    vector<int> A(N);
    for (int i = 0; i < N; ++i) cin >> A[i];
    
    for (int v = 0; v <= 9; ++v) {
        // A の中に v があるかどうかを調べる
        bool exist = false;
        
        for (int i = 0; i < N; ++i) {
            if (A[i] == v) {
                exist = true;
            }
        }
        
        if (exist) {
            cout << v << endl;
        }
    }
}

 

解法 (2):バケット

解法 1 よりも高速な解法がある。それは次の配列 (しばしばバケットと呼ばれる) を用意することだ。公式解説もこの方法を解説している。


exist[v] ← 数列の中に値  v の要素が存在するかどうかを表すブール値 (True / False)


最初は配列 exist 全体を False で初期化しておき、for 文を 1 回回して、各 i に対して

exist[A[i]] = true;

とすることで、配列 exist が求められる。

配列 exist が求められたら、あとは exist[0], exist[1], ..., exist[9] を順に調べて True である場合には対応する値を出力すればよい。

余談

なお、解法 (1) よりも解法 (2) の方が、計算量の観点では優れている。計算量については次の記事などで解説している。

qiita.com

コード

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

int main() {
    int N;
    cin >> N;
    vector<int> A(N);
    for (int i = 0; i < N; ++i) cin >> A[i];
    
    // 値 v があるかどうか
    vector<bool> exist(10, false);
    for (int i = 0; i < N; ++i) exist[A[i]] = true;
    
    // 値 v を順に調べる
    for (int v = 0; v <= 9; ++v) {
        if (exist[v]) {
            cout << v << endl;
        }
    }
}

JOI 一次予選 2024 (第 1 回) C - ハミング距離 (8Q, 難易度 2)

for 文の練習をしよう!

問題概要

長さ  N の文字列  S, T が与えられる。これらの文字列のハミング距離を求めよ。

ハミング距離とは、 S_{i} \neq T_{i} となる  i の個数のことを指す。

解法

for 文の出番です。S[i] != T[i] であるような i の個数を求めればよいでしょう。

それでは、具体的なコードについて考えます。

上図のように、答えとなる変数 res を用意して、res = 0 と初期化しておきます。そして、for 文を用いて、各 i に対して

  • S[i] != T[i] であるならば、res を 1 増やす (下のコードでは ++res としています)
  • そうでないならば、何もしない

というようにすればよいです。

コード

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

int main() {
    int N;
    string S, T;
    cin >> N >> S >> T;
    
    int res = 0;
    for (int i = 0; i < N; ++i) {
        if (S[i] != T[i]) {
            ++res;
        }
    }
    cout << res << endl;
}

JOI 一次予選 2024 (第 1 回) B - 和の判定 (8Q, 難易度 1)

上手に場合分けしよう!

問題概要

3 個の整数  A, B, C が与えられる。

これらのうちの 1 個が、残りの 2 個の和として表せるときは 1 を出力し、そうでないときは 0 を出力せよ。

解法

「3 個の整数  A, B, C のうちの 1 個」が残りの 2 個の和として表せる条件を考えましょう。

その「1 個」が  A である場合と、 B である場合と、 C である場合に分けて考えます。

  •  A である場合:
    • 残りの 2 個の和は  B + C なので、1 になる条件は  A = B + C と表せます
  •  B である場合:
    • 残りの 2 個の和は  C + A なので、1 になる条件は  B = C + A と表せます
  •  C である場合:
    • 残りの 2 個の和は  A + B なので、1 になる条件は  C = A + B と表せます

これらより、求める条件は


 A = B + C または  B = C + A または  C = A + B


と表せます。この条件を満たすならば 1 を出力し、満たさないならば 0 を出力しましょう。

コード

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

int main() {
    int A, B, C;
    cin >> A >> B >> C;
    
    if (A == B + C || B == C + A || C == A + B) {
        cout << 1 << endl;
    } else {
        cout << 0 << endl;
    }
}

JOI 一次予選 2024 (第 1 回) A - 果物 (10Q, 難易度 1)

JOI 一次予選の問題は本当に教育的なものが多い!

問題概要

リンゴが  X 個、ミカンが  Y 個、バナナが 3 個ある。

リンゴとミカンとバナナが合わせて何個あるかを求めよ。

解法

答えは  X + Y + 3 個です。

プログラムを書くときには、次のようにして解くことができます。

  • 2 個の整数値 X, Y を標準入力から受け取る
  • X + Y + 3 を標準出力する

コード

ここでは C++ のコードを示します。C++ では標準入力に cin、標準出力に cout が使えます。

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

int main() {
    int X, Y;
    cin >> X >> Y;
    cout << X + Y + 3 << endl;
}