zoukankan      html  css  js  c++  java
  • 从 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);
    }
  • 相关阅读:
    Git:五、操作远程仓库
    Git:四、连接GitHub远程仓库
    Git:三、工作原理
    Git:二、本地文件操作
    Git:一、简介&安装Git 2.20.1——Mac&Win
    Web前端:博客美化:四、网易云音乐单曲播放器
    设计模式之代理模式(proxy pattern)
    OpenCC的编译与多语言使用
    唯一标识符漫谈
    vscode local attach 和 remote debug
  • 原文地址:https://www.cnblogs.com/happylong/p/4504341.html
Copyright © 2011-2022 走看看