C++
 Computer >> コンピューター >  >> プログラミング >> C++

【C++】nから始まり隣接要素の差がk未満となるすべての数列を出力する方法

問題の概要

この問題では、3つの変数 n(開始値)、s(数列の長さ)、k(差の上限)が与えられます。求めるのは、「数 n から始まり、長さ s を持ち、隣り合う要素どうしの絶対差が k 未満である」ような、考えられるすべての数列を出力することです。

入力例と出力例

まずは具体例を見ながら、問題をより深く理解しましょう。

Input: n = 3, s = 3, k = 2
Output:
3 3 3
3 3 4
3 3 2
3 4 4
3 4 5
3 4 3
3 2 2
3 2 3
3 2 1

この例では、先頭が 3 で始まる長さ 3 の数列のうち、隣接する要素の絶対差が 2 未満(つまり 0 または 1)になっているものがすべて列挙されています。

解法のアプローチ

この問題のポイントは、隣接要素間の絶対差を k 未満に抑える必要がある点です。そのためには、次の要素として「現在の値より大きい値(正の差)」と「現在の値より小さい値(負の差)」の両方を候補として生成します。

具体的な手順は以下のとおりです。

  • n から開始し、各位置の要素に対して再帰呼び出しを行います。
  • 0 から k-1 までのループで、現在の数値に加算した値を次の要素の候補とします(正方向)。
  • 同様に、1 から k-1 まで減算した値も候補とします(負方向)。

このように再帰とバックトラッキングを組み合わせることで、条件を満たすすべての数列を網羅的に列挙できます。

C++での実装例

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

void printConsecutiveNumbers(vector<int>& v, int n, int s, int k){
    // 長さ s の数列が完成したら出力する
    if (s == 0) {
        for (int i = 0; i < v.size(); i++)
            cout << v[i] << " ";
        cout << endl;
        return;
    }
    // 正方向:現在の値に 0 ~ k-1 を加えた候補を試す
    for (int i = 0; i < k; i++) {
        v.push_back(n + i);
        printConsecutiveNumbers(v, n + i, s - 1, k);
        v.pop_back(); // バックトラッキング
    }
    // 負方向:現在の値から 1 ~ k-1 を引いた候補を試す
    for (int i = 1; i < k; i++) {
        v.push_back(n - i);
        printConsecutiveNumbers(v, n - i, s - 1, k);
        v.pop_back(); // バックトラッキング
    }
}

int main(){
    int n = 3, s = 3, k = 2;
    cout << "The sequence is :\n";
    vector<int> v;
    v.push_back(n); // 先頭に n を設定
    printConsecutiveNumbers(v, n, s - 1, k);
    return 0;
}

コードの解説

printConsecutiveNumbers 関数は、残りの要素数 s が 0 になった時点で、ベクトルに格納された数列を出力して処理を終了します(再帰の基底ケース)。それ以外の場合は、正方向(n + i)と負方向(n - i)の両方について再帰的に探索を行い、push_backpop_back によるバックトラッキングで状態を元に戻しながら、条件を満たすすべての組み合わせを列挙します。

なお、このアルゴリズムの時間計算量は、各ステップで最大 2k-1 通りの候補を生成するため、おおよそ O((2k−1)s) となります。s や k が大きくなると組み合わせ数が急増する点に注意してください。

実行結果

上記のコードを実行すると、次の出力が得られます。

The sequence is :
3 3 3
3 3 4
3 3 2
3 4 4
3 4 5
3 4 3
3 2 2
3 2 3
3 2 1

期待どおり、3 から始まり長さ 3、隣接要素の絶対差が 2 未満となるすべての数列が出力されていることが確認できます。

  1. C++で木構造のノード数が奇数・偶数となるレベルをすべて出力する方法

    この記事では、木(ツリー)構造が与えられたときに、各レベルに含まれるノードの数を調べ、その数が奇数であるレベルと偶数であるレベルをそれぞれ出力する方法を、C++のサンプルコード付きで解説します。 問題の概要 まず、具体的な例を使って概念を確認しましょう。次のような木構造を考えます。 出力: ノード数が奇数のレベル:1, 3, 4 ノード数が偶数のレベル:2 解説: 第1レベルにはノードが1個(奇数)、第2レベルには2個(偶数)、第3レベルには3個(奇数)、第4レベルには1個(奇数)存在します。そのため、奇数となるのは「1, 3, 4」のレベル、偶数となるのは「2」のレベルです。 解き方

  2. 【C++】配列内の隣接する要素同士の絶対差を求める方法

    この記事では、配列内の隣接する2つの要素のペアごとに絶対差(絶対値の差)を求める方法を解説します。配列に n 個の要素が含まれている場合、結果として得られる配列には n-1 個の要素が格納されます。例えば、配列の要素が {8, 5, 4, 3} である場合、計算結果は次のようになります。|8−5| = 3、|5−4| = 1、|4−3| = 1アルゴリズムpairDiff(arr, n)begin    res := 結果を格納するための配列    for i in range 0 to n-2, do       res[