//////////////////////////////////////////////////////////////////////////
//
// 算法 : 从 N 个数字中选取 M 个, 打印所有可能组合
//
//////////////////////////////////////////////////////////////////////////
//
// 使用一个辅助数组 aux[1..M] 用来记录 input[1..N] 中被选中元素的索引
// 比如 input[i] 被选中, 那么中会有一项 aux[*] = i
//
//
// 从后向前计算:
// 基本思想是, 从 N 个元素中选取 M 个, 首先选取第 M 个, 然后在从剩下的选取 M - 1 个。
//
// 对于 aux[m] 有选择的数目: (从 input 的没被用到的最后一个) ~ input[m]。
// 止于 input[m] 的原因是因为再向前的话, aux[0..m-1] 可以选择的 input 数目会不足。
//
// 这里 m 表示的是还剩几个元素没有选, 初始化的时候值为 M
void combination1(char input[], const int N, int aux[], int m, const int M, int & counter)
{
for (int i = N; i >= m; i--) // 从后向前
{
if (m >= 1)
{
aux[m - 1] = i - 1; // 选取 input[i-1]
}
if (m > 1) // 还没选完, 从 input[0..i-1] 中选取剩下的 m-1 个元素
{
combination1(input, i - 1, aux, m - 1, M, counter);
}
else // 选择完毕
{
counter++;
cout << "{ "; // 根据 aux[j] 指定的下标, 输出 input[aux[j]]
for (int j = 0; j <M; j++)
{
cout << input[aux[j]] << " ";
}
cout << "}" << endl;
}
}
}
void combination1(char input[], const int N, const int M)
{
int * aux = new int[M];
memset(aux, 0, sizeof(int) * M);
int counter = 0;
combination1(input, N, aux, M, M, counter);
cout << "total : " << counter << endl;
}
//
// 从前向后计算:
// 基本思想是: 从 N 个元素选取 M个, 首先选取第 1 个, 其余 M - 1 个元素从剩下的元素中选取。
//
// [begin*************X******N-1]
// [0....m******M-1]
// N - 1 - X = M - 1 - m
// X = N - M + m
// aux[m] 选择范围: [input[begin], input[X]]
//
// 这里的 m 表示选择了多少个元素, 初始值为 0
void combination2(char input[], const int begin, const int N, int aux[], int m, const int M, int & counter)
{
for (int i = begin; i <= N - M + m; i++) // 从前向后
{
if (m < M)
{
aux[m] = i; // 选择第 m 个元素
}
if (m < M - 1) // 没选择完, 从余下的 input[i+1..N] 中选择余下的元素
{
combination2(input, i + 1, N, aux, m + 1, M, counter);
}
else
{
counter++;
cout << "[ ";
for (int j = 0; j <M; j++)
{
cout << input[aux[j]] << " ";
}
cout << "]" << endl;
}
}
}
void combination2(char input[], int N, int M)
{
int * aux = new int[M];
memset(aux, 0, sizeof(int) * M);
int counter = 0;
combination2(input, 0, N, aux, 0, M, counter);
cout << "total : " << counter << endl;
}
//
// 回溯法
// flag 大小和 input 相同
// flag[i] 用来记录 input[i] 是否被选中
// n 表示有多少个 input 元素参与了选择
// m 表示选择了多少个元素
void back_tracking(char input[], bool flag[], const int N, const int M, int n, int m, int & counter)
{
if (m == M) // 选满了 M 个
{
counter++;
cout << "< ";
for (int j = 0; j <N; j++)
{
if (flag[j])
{
cout << input[j] << " ";
}
}
cout << ">" << endl;
}
else if (m >= M || n >= N ) // n >= N 说明把 input 挑了个遍, 也没凑齐 M 个元素
{
return;
}
else
{
flag[n] = 1; // 选取了 input[n]
back_tracking(input, flag, N, M, n + 1, m + 1, counter);
flag[n] = 0; // 没有选择 input[n]
back_tracking(input, flag, N, M, n + 1, m, counter);
}
}
void back_tracking(char input[], int N, int M)
{
bool * flag = new bool[N];
memset(flag, 0, sizeof(bool) * N);
int counter = 0;
back_tracking(input, flag, N, M, 0, 0, counter);
cout << "total : " << counter << endl;
}
void select_M_from_N()
{
char input[] = "abcdef";
//
// 算法一
combination1(input, strlen(input), 3);
//
// 算法二
combination2(input, strlen(input), 3);
//
// 算法三
back_tracking(input, strlen(input), 3);
}