全探索の基礎になるような再帰関数の使い方!
問題概要
以下の条件を満たす長さ の整数列を辞書順で小さい方から順に出力せよ。
- 番目の要素は 以上 以下
- 総和が の倍数
制約
考えたこと
このように、長さ のさまざまな数列を生成する方法については、次の記事に詳しく書いた!
今回も、ここで書いた方法が使える。まず、 番目の要素が 以上 以下であるような数列を列挙するのは、次のような再帰関数で実現できる。なお、R
は何らかの方法で与えられるものとする(下のコードでは、再帰関数の引数にしている)。
void dfs(vector<int> &A) { // 終端条件 --- N 回までいったら終わり if (A.size() == R.size()) { for (int i = 0; i < A.size(); ++i) cout << A[i] << " "; cout << endl; return; } for (int v = 1; v <= R[A.size()]; ++v) { A.push_back(v); dfs(A, R); A.pop_back(); // これが結構ポイント } }
ここまでできたら、あとは終端条件を処理する部分において、「総和が の倍数のときに限り出力する」というよううにすればよい。
計算時間を見積もろう。最悪ケースは次の通りである。
このとき、出力する数列の個数は 個となる。この程度の分量ならば余裕で間に合う。
コード
まず、入力として与えられる は再帰関数の引数とした。さらに、 の途中経過における総和 sum
も引数として与えることとした。
#include <bits/stdc++.h> using namespace std; void dfs(vector<int> &A, int K, const vector<int> &R, int sum) { // 終端条件 --- N 回までいったら終わり if (A.size() == R.size()) { if (sum % K == 0) { for (int i = 0; i < A.size(); ++i) cout << A[i] << " "; cout << endl; } return; } for (int v = 1; v <= R[A.size()]; ++v) { A.push_back(v); dfs(A, K, R, sum + v); A.pop_back(); // これが結構ポイント } } int main() { int N, K; cin >> N >> K; vector<int> R(N); for (int i = 0; i < N; ++i) cin >> R[i]; vector<int> A; dfs(A, K, R, 0); }