从 N 个元素中选取 M 个元素, 有多少种组合

//////////////////////////////////////////////////////////////////////////
//
// 算法 : 从 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);
}
原文地址:https://www.cnblogs.com/happylong/p/4504341.html