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

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

JOIG 2023 B - 絶対階差数列 (AOJ 0758, 難易度 2)

いわゆる「愚直シミュレーション」と呼ばれる分野の問題ですね。問題文で指示されたことを、とにかく愚直に忠実に実装できるかが問われています。地味な印象を受けるかもですが、大切なスキルです!

問題概要

黒板に、はじめ  N 個の整数値  A_{1}, A_{2}, \dots, A_{N} が書かれている。

この数列に対して「階差数列をとる」という操作を  N-1 回実行すると、最終的には「ただ 1 つの整数値からなる数列」が得られる。この整数値を答えよ。

なお、階差数列をとるという操作は、数列  b_{1}, b_{2}, \dots, b_{m} (長さ  m) に対して、数列  |b_{1} - b_{2}|, |b_{2} - b_{3}|, \dots, |b_{m-1} - b_{m}| (長さ  m-1) を得る操作を指す。

解法:関数を用意するのがオススメ!

このように「数列や盤面などなんらかの対象物に対して、操作 A を繰り返し実行していく」という設定の問題では、操作 A を実行する部分を関数として切り出すのが有効だと思います。

C++ だと、こんな感じの関数を用意するとよいでしょう。

// 数列 A の階差数列を求める関数
vector<int> kaisa(vector<int> B) {
    int M = (int)B.size();
    vector<int> res(M-1);  // サイズは 1 減少する
    
    // 階差を順に求めていく
    for (int i = 0; i < M-1; ++i) {
        res[i] = abs(B[i+1] - B[i]);
    }
    return res;
}

この関数を  N-1 回呼び出せばよいです。具体的には次のコードのように実装できます。

解答例コード

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

// 数列 A の階差数列を求める関数
vector<int> kaisa(vector<int> B) {
    int M = (int)B.size();
    vector<int> res(M-1);  // サイズは 1 減少する
    
    // 階差を順に求めていく
    for (int i = 0; i < M-1; ++i) {
        res[i] = abs(B[i+1] - B[i]);
    }
    return res;
}

int main() {
    // 入力受け取り
    int N;
    cin >> N;
    vector<int> A(N);
    for (int i = 0; i < N; ++i) cin >> A[i];
    
    // 階差数列を求める処理を N-1 回実行する
    for (int i = 0; i < N-1; ++i) {
        A = kaisa(A);
    }
    
    // 最後には A は「ただ 1 つの要素からなる vector」となる
    // その 1 つの要素値を出力する
    cout << A[0] << endl;
}