数学では重複組み合わせ問題とも呼ばれるらしい.
背景
https://atcoder.jp/contests/abc268/tasks/abc268_d
この問題の回答テクニックの中に, 「_
を文字列と文字列の間に入れる組み合わせを全列挙する」というものがあった.
公式解説ではDFSを使っているが, 再帰ではない方法を考えたい.
問題設定
整数n
, m
が入力として与えられる.
n
個の飴をm
人に配るときの組み合わせを全列挙してください.
配られない人がいる場合や, 飴が誰にも配られずに余る場合も考慮してください.
解説
重複組合せを理解した上で, C++ の next_permutation
を利用する
next_permutation
は, 配列を与えると辞書順で全列挙してくれる関数. do-whileループの中で使うのがおすすめ.
do {
// whileループの中でfieldの辞書順が全列挙される
} while (next_permutation(field.begin(), field.end()));
辞書順の最後に到達した配列をnext_permutationに与えるとfalse
が返り値になるのでループを抜けることができる.
なお, 予め配列はソートしておく必要がある.
解法 (C++)
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
// 飴と区切り棒をfieldに定義する
// あらかじめ昇順に並べる必要がある
// sort(field.begin(), field.end());
vector<int> field;
for (int i = 0; i < n; i++) field.push_back(0);
for (int i = 0; i < m; i++) field.push_back(1);
do {
// 1つの組み合わせを保存しておく配列
vector<int> ans;
int candy_num = 0;
for (int i = 0; i < field.size(); i++) {
if (field[i] == 0) candy_num++;
// 区切り棒が来たらその人は終了
if (field[i] == 1) {
ans.push_back(candy_num);
candy_num = 0;
}
}
ans.push_back(candy_num);
// 1つの組み合わせを出力
for (int i = 0; i < ans.size() - 1; i++) {
cout << ans[i] << " ";
}
cout << endl;
} while (next_permutation(field.begin(), field.end()));
}
入力
3個の飴を2人に配る
3 2
出力
例えば3 0
はそれぞれ3個と0個飴をあげることを表す
3 0
2 1
2 0
1 2
1 1
1 0
0 3
0 2
0 1
0 0