公式

C - Enumerate Sequences 解説 by physics0523


\(1\) 以上 \(5\) 以下の整数からなる長さ \(8\) の数列」は \(5^8=390625\) 個です。 出力すべき数列の個数はこの数以下なので、出力が大きすぎる心配をする必要はありません。

方針1. 再帰を使って列挙する

  • 最初に \(1\) つ目の要素を小さい順に固定する
  • \(1\) つ目の要素を固定した下に再帰を呼び、 \(2\) つ目の要素を小さい順に固定する
  • \(\dots\)
  • 要素が出そろったら、総和が \(K\) の倍数であるかを判定した後出力する

というような再帰を実装するとこの問題に正解できます。

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;

int n,k;
int r[8];
int seq[8];

void solve(int lv){
  if(lv==n){
    int s=0;
    for(int i=0;i<n;i++){ s+=seq[i]; }
    if(s%k==0){
      for(int i=0;i<n;i++){
        if(i){cout << " ";}
        cout << seq[i];
      }cout << "\n";
    }
    return;
  }
  for(int i=1;i<=r[lv];i++){
    seq[lv]=i;
    solve(lv+1);
  }
}

int main(){
  cin >> n >> k;
  for(int i=0;i<n;i++){
    cin >> r[i];
  }
  solve(0);
  return 0;
}

方針2. 数列に対応する整数をデコードする

\(K\) の倍数 」という条件を無視すれば、数列として考えられるものは \(P=R_1 \times R_2 \times \dots \times R_N\) 通りです。
実は、 \(0\) から \(P-1\) までの整数から以下の通りに数列を復元すれば、その数列は辞書順に並びます。

  • 整数 \(t\) から復元される数列を \(S\) とする。また、変数 \(x \leftarrow t\) を用意する。
  • \(i = N\) から \(1\) まで、次を繰り返す。
    • \(S_i \leftarrow (x \% R_i)+1\) とする。 ( \(p \% q\)\(p\)\(q\) で割った余り)
    • その後、 \(x\)\(\displaystyle \frac{x}{R_i}\) (小数点以下切り捨て) に置き換える。

直感的には、数列は特殊な N 進法表記に対応します。
\(R=(6,3,2,5)\) とすると、後ろの要素(下の位)から順に \(1\) の位、 \(5\) の位、 \(5 \times 2\) の位、 \(5 \times 2 \times 3\) の位 と並んでいると捉えることができます。ここで、 \(A=(1,3,1,2)\)\(0 \times 30 + 2 \times 10 + 0 \times 5 + 1\) に対応します (この際、 \(1\) から \(R_i\) までの各要素を \(0\) から \(R_i-1\) までに修正する必要があることに注意してください)。

他にも、この方針の亜種として別のデコード方法を採用し最後に全ての数列をソートするという方法もあります。 ( 例えば C++ では、 vector<vector<int>> をソートすると中身の vector<int> を辞書順に並べることができます)

実装例 (C++):

#include<bits/stdc++.h>

using namespace std;

int n,k;
int r[8];
int seq[8];

int main(){
  cin >> n >> k;
  int tot=1;
  for(int i=0;i<n;i++){
    cin >> r[i];
    tot*=r[i];
  }
  for(int t=0;t<tot;t++){
    int x=t;
    int s=0;
    for(int i=n-1;i>=0;i--){
      seq[i]=(x%r[i])+1;
      s+=seq[i];
      x/=r[i];
    }
    if(s%k==0){
      for(int i=0;i<n;i++){
        if(i){cout << " ";}
        cout << seq[i];
      }cout << "\n";
    }
  }
  return 0;
}

投稿日時:
最終更新: