////////////////////////////////////////////////////////////////////////// // // 算法 : 从 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); }