zoukankan      html  css  js  c++  java
  • 排列组合之线性排列

    1、问题
    1.1 袋中取球

    image
    袋子里有4个球,分别编号为{1, 2, 3, 4},依次取出,按照取出的先后从左至右排列,会得到一个不同的数字(如 1 2 3 4,有点像双色球开奖),求输出所有的数字组合。

    1.2 不重复的数
    有4个数字{0, 1, 2, 3},问用这4个数字能组成多少种不能的4位数(0123也算,因为我们也可以用{1, 2, 3, 4})?

    2、排列组合
    上述2个问题实际都是一个排列组合的问题,首先我们来计算一共有多少种组合

     

    取第1个球

    取第2个球

    取第3个球

    取第4个球

    能取球的次数

    4

    3

    2

    1

    组合数 = 4 * 3 * 2 * 1 = 4!
    即n个球的组合总数 = n!
    我们可以验证下:
    1个球的组合:{0} = 1
    2个球的组合:{0, 1}, {1, 0} = 2
    3个球的组合:{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0} = 6

    3、如何输出线型排列组合
    根据我们刚才验证组合的过程,我们发现我们在取第n个球时,我们总是从最小的数字0开始,如果该球已被取过了,则跳过继续遍历下一个数字,不管是取第0个球还是第n个,都是如此,因此我们只要知道第n个球的数字,那么下一个球

    的数字都可以用相同的方法求出来,直到只剩一个球为止。
    算法公式f(x):
    1> 当取第x个球时,假设前x - 1个球已取{a, b, c, ...}个数字,放在容器S中(当然,开始时,容器S是空的)
    2> 从{0, ..., n-1}循环
        判断第i个数是否已被取过,即是否在S中
        如果在,则跳过这个数
        如果不在,则取出这个球,并添加到容器S中,表示该球已被占用
        这时,该输出下一个球(x+1)个球了,我们用同样的算法f(x+1)
        当x之后所有的球都被取出后,这时我们要继续循环下一个不在容器中的数字,但要注意,因为第x个球下次循环的数字将要改变了(i→i+1),球i将不再被占用,以供取后续的球之用,所以,我们要讲i这个球从占用容器S中取出
    3> 很明显,这是一个递归过程,算法是有有限性的,那么什么时候这个递归停止呢
        我们知道,当取出后一个球(n-1)时,袋子里就没有球了,再取下一个时,本次递归就可以结束了

    4. 算法

    注意:根据编程习惯,第一个球的编号和首次取球都是以0开始

    //每个递归,都遍历一遍 vectorBall 中的空值,然后下次递归,递归完后,将原来设定的值设为0
    //nIndex = 取第x个球
    void    CPermutation::SetBallNum(int nIndex, std::vector<int>& vectorBall, std::vector<int> vectorBallSet, std::vector<std::vector<int>>& vectorPermutation)
    {
        int nEmptyBallNum = 0;
        int nBallNum = vectorBall.size();
    
        if (nBallNum == nIndex)
        {
            vectorPermutation.push_back(vectorBallSet);
            return;
        }
    
        for (int i = 0; i < nBallNum; i++)
        {
            //第x个球,最多有 nBallNum - x 个空位
            if (nEmptyBallNum >= (nBallNum - nIndex))
            {
                break;
            }
            //如果找到空值
            if (0 == vectorBall[i])
            {
                vectorBall[i] = 1;
                nEmptyBallNum++;
                vectorBallSet[nIndex] = i;
                SetBallNum(nIndex + 1, vectorBall, vectorBallSet, vectorPermutation);
                vectorBall[i] = 0;        //将尝试完的值设为0,再尝试下一个
            }
        }
    }
    
    //有n个编号为{0, n-1}的小球,依次取出,按照取出的先后从左至右排列,会得到一个不同的数字,打出所有的数字组合
    //类似问题,用{0, n-1}个数,能组成多个数字不重复的n位数
    bool    CPermutation::PermutationBall(int nBallNum /* = 4 */)
    {
        printf("Ball num = %d
    ", nBallNum);
    
        std::vector<int>    vectorBallSet(nBallNum, 0);
        std::vector<int>    vectorBall(nBallNum, 0);
        std::vector<std::vector<int>> vectorPermutation;
        SetBallNum(0, vectorBall, vectorBallSet, vectorPermutation);
        PrintPermutation(vectorPermutation);
    
        return    true;
    }
  • 相关阅读:
    hdu1151 二分图(无回路有向图)的最小路径覆盖 Air Raid
    二分图多重匹配问题
    二分图最大匹配问题及其扩展
    ZOJ3741 状压DP Eternal Reality
    POJ2699:The Maximum Number of Strong Kings(枚举+贪心+最大流)
    POJ2396:Budget(带下界的网络流)
    POJ2391:Ombrophobic Bovines(最大流+Floyd+二分)
    POJ1637:Sightseeing tour(混合图的欧拉回路)
    URAL1277 Cops and Thieves(最小割)
    Leetcode 44. Wildcard Matching
  • 原文地址:https://www.cnblogs.com/organic/p/6033440.html
Copyright © 2011-2022 走看看