排列组合问题之圆形分布

1、问题
1.1 团团坐
有一张圆桌,坐了A,B,C,D四个人,已知,D在A的右边,C在D的对面,请问A,B,C,D,的坐次?

解答:这个问题相对简单,我们纸上画一画,就能画出他们的可能的位置了

但是,可能还有一种解,比如我们把A,B,C,D依次右转一个位,也是满足条件的,而且只要保持他们的相对位置不变,依次右转n个位都是问题的解,而且还有个有趣的事情,当他们转了一圈(即右转4个位)后,他们右回到原位了

2、圆形分布
上面这个问题就是一种圆形分布,那么他和直线分布的区别在哪里呢?又有什么联系呢?
上面文章《排列组合问题之线型排列》中讲过,当有4个球时,可能的排列共有4! = 24种,那么我们把A,B,C,D四个人的坐位分别标为{0, 1, 2, 3}的号,那么A,B,C,D四个人可能坐的位置就是一个线型排列。

假设我们用计算机来解析这个问题,给出一种可能的分布方式。我们的思路是:
1> 列出所有可能的分布
2> 然后解析每种组合是否满足题目的要求
当然,我们也可以每找到一种组合,就判断一次是否满足题目的要求,这样找到一种后就可以退出了,可以减少时间复杂度。
如果我们按线型排列处理,我们一共需要找出24种排列出来,根据前面的解析可知,某一种排列还有3种排列都可以满足题目的解得,我们只需要求出一种解即可,找出24种比较费时间,而且当问题复杂化后,比如是一个16边形的桌子,给出上述类似的问题,那么最坏情况下共需要列出16!=20922789888000种可能,如果我们能够去掉其中重复的,就可以减少计算。如我们这里实际只需要列出24 / 4 = 6种分布即可

3、如何找出线型分布中不一样的分布
我们再来看看问题的解答:A,B,C,D四个人的坐次 = {0, 2, 3, 1}
假设A固定不动,那么B, C, D可能的坐次仍是一个线型分布,即共有3!=6种,因为A不动,所有B,C,D不可能发生转动了,所以不会有上面多个解的问题
这时我们每右转一次A,那么上面的6种分布又可以得到新的6种线型分布,但他们和转动前的分布都是问题的解(因为他们的相对位置不变,而问题给出的条件是相对位置),所以对于圆形分布,即是把线型分布的首位连接成一个圆,圆转动后他们的位置仍然会保持相对不变,那么我们只需要求出A=0,{B,C,D}的线型分布组合起来的解,就可以用来判定题目给出的条件

4、圆形分布算法

为了减少篇幅,创建线型分布的函数 SetBallNum() 见《排列组合问题之线型排列》

为了处理方便,重复利用代码,生成圆形分布时,我们是固定最后一位不动的,即D的位置不动

bool    CPermutation::CreateCirclePermutaion(int nNum, std::vector<std::vector<int>>& vectorPermutation)
{
    //先创建nNum - 1个位的的直线排布
    std::vector<int>    vectorBallSet(nNum - 1, 0);
    std::vector<int>    vectorBall(nNum - 1, 0);
    SetBallNum(0, vectorBall, vectorBallSet, vectorPermutation);

    //然后将nNum - 1的位添加到最后
    for (int i = 0; i < vectorPermutation.size(); i++)
    {
        vectorPermutation[i].push_back(nNum - 1);
    }

    return    true;
}

说明:为了减少代码,处理方便,算法中用到了STL的vector容器,需要少许STL容器使用的相关知识,当然用数组也是可以实现的,代码会略麻烦

5、如何表达A,B,C,D的相对位置
5.1 什么是x在y的右边?
我们来换一张相对好看一点的图:

图中B在A的右边,他们的位置是(0, 1),C在B的右边他们的位置是(1, 2)....

 
A, B


B, C


C, D


D, A

相对位置
(0, 1)


(1, 2)


(2, 3)


(3, 0)

通过C之前的位置我们可以总结出,所谓x在y的右边就是f(x)+1 = f(y),这里f(x)是指x的位置号。
但当到D后,我们发现上面这个公式不适用了A也在D的右边,但他们的位置却是(3, 0),该如何处理这种情况?
我们再仔细看,当到D后,他的右边的位置理应是4,但由于是圆形分布,最后一位和第一位首位相接了,这实际上就和时钟转满了一圈是一样的,这时4要变成0了,这实际就是一个模的运算,取其余数即可,即:
f(x)+1 = f(y)%4 = f(y)%桌子的边数

5.2 什么是x在y的对面?
再看一次上面那张图, C在A的对面,他们的位置是(0, 2),D在B的对面,他们的位置是(1, 3),因为如果2个人相对,那么他们之间就隔了一半的人
于是我们能总结出公式:f(y)-fx(x) = 4 / 2 = 2
但是又发现也可以说A在C的对面,f(a)-f(c) = -2,修改下公式:
|f(y)-f(x)| = 2 = 4 / 2 = 桌子的边数/2

6、自动判断算法

bool    CPermutation::TestCircleDesk()
{
    int    nSideNum = 4;
    std::vector<std::vector<int>> vectorPermutation;

    CreateCirclePermutaion(nSideNum, vectorPermutation);
    PrintPermutation(vectorPermutation);

    //{A, B, C, D}对应分布中的{0, 1, 2, 3}
    //遍历所有的分布
    for (int i = 0; i < vectorPermutation.size(); i++)
    {
        std::vector<int>&    vectorCirclePermutation = vectorPermutation[i];
        //根据输入条件做判断
        //f(x)+1 = f(y)%4
        //D在A的右边,即f(0)+1 = f(3)%4
        //如果不满足该条件,则继续下一组
        if ((vectorCirclePermutation[0] + 1) != (vectorCirclePermutation[3] % nSideNum))
        {
            continue;
        }

        //|f(y)-f(x)| = 2 = 4 / 2
        //C在D的对面,即|f(2)-f(3)| = 2 = 4 / 2
        if (abs(vectorCirclePermutation[2] - vectorCirclePermutation[3]) == nSideNum / 2)
        {
            printf("Find matched permutation : ");
            for (int j = 0; j < nSideNum; j++)
            {
                printf("%d ", vectorCirclePermutation[j]);
            }
            printf("\n");
            return    true;
        }

    }

    return    false;
}

  

说明:算法实现中是固定最后一位不动的,即D的位置不动,所以最后一位都是3(即D始终在3号位),但该解也是符合原问题的。

时间: 2024-12-07 07:28:14

排列组合问题之圆形分布的相关文章

【程序员眼中的统计学(5)】排列组合:排序、排位、排

排列组合:排序.排位.排 作者 白宁超 2015年10月15日18:30:07 摘要:程序员眼中的统计学系列是作者和团队共同学习笔记的整理.首先提到统计学,很多人认为是经济学或者数学的专利,与计算机并没有交集.诚然在传统学科中,其在以上学科发挥作用很大.然而随着科学技术的发展和机器智能的普及,统计学在机器智能中的作用越来越重要.本系列统计学的学习基于<深入浅出统计学>一书(偏向代码实现,需要读者有一定基础,可以参见后面PPT学习).正如(吴军)先生在<数学之美>一书中阐述的,基于统

概率论1 计数-排列-组合

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 概率 概率论研究随机事件.它源于赌徒的研究.赌博中有许多随机事件,比如投掷一个骰子,是否只凭运气呢? 赌徒逐渐发现随机事件的规律.投掷两个骰子是常见的赌博游戏.如果重复很多次,那么总数为2的次数会比总数7的次数少.这就是赌徒把握到的规律:尽管我无法预知事件的具体结果,但我可以了解每种结果出现的可能性.这是概率论的核心. "概率"到底是什么?这在数学上还有争议."

HDU--5396(区间dp+排列组合)

做这道题的时候,想到会不会是dp,然后发现dp可做,但是一直被自己坑到死. 枚举最后合并的那个位置,然后对于加减号的,分成的前后两个部分都有不同的组合方法, (a1+a2........) +  (b1,b2.............)         对于每个a,被加b的个数的阶乘次 ,对于每个b,被加a的个数的阶乘次 减法同理 乘法特殊一点 (a1+a2........) *  (b1,b2.............)  乘法分配率,直接将两部分的总和相乘即可 想到这些还远远没有结束,因为最

排列组合

(常考)错位排列 有N封信和N个信封,每封信都不装在自己信封里的排列种数记作Dn,则 D1=0,D2=1,D3=2,D4=9,D5=44,D6=265 一.相邻问题---捆绑法 不邻问题---插空法 对于某几个元素不相邻的排列问题,可先将其他元素排好,再将不相邻元素在已排好的元素之间及两端空隙中插入即可. [例题1]一张节目表上原有3个节目,如果保持这3个节目的相对顺序不变,再添进去2个新节目,有多少种安排方法? A.20 B.12 C.6 D.4 [答案]A. [解析] 以下内容需要回复才能看

hdu 1799 (循环多少次?)(排列组合公式)

循环多少次? Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 3051    Accepted Submission(s): 1117 Problem Description 我们知道,在编程中,我们时常需要考虑到时间复杂度,特别是对于循环的部分.例如, 如果代码中出现 for(i=1;i<=n;i++) OP ; 那么做了n次OP运算

排列组合问题

一.不同元素子集问题 78. Subsets Given a set of distinct integers, nums, return all possible subsets. 给定一组非重复数字,求出所有可能的子集 解析: 例如 [1,2,3],解法: 首先放[],然后往已有的[]中放1 1. 首先放1 此时已有[ [], 1 ] 2. 然后对[ [], 1 ] 放2 于是此时有 [ [], [1], [2], [1,2] ] 3. 然后对[ [], [1], [2], [1,2] ]

【noi 2.6_9288】&amp;【hdu 1133】Buy the Ticket(DP / 排列组合 Catalan+高精度)

题意:有m个人有一张50元的纸币,n个人有一张100元的纸币.他们要在一个原始存金为0元的售票处买一张50元的票,问一共有几种方案数. 解法:(学习了他人的推导后~) 1.Catalan数的应用7的变形.(推荐阅读:http://www.cnblogs.com/chenhuan001/p/5157133.html).P.S.不知我之前自己推出的公式“C(n,m)*C(2*m,m)/(m+1)*P(n,n)*P(m,m)”是否是正确的. (1)在不考虑m人和n人本身组内的排列时,总方案数为C(m+

用递归写排列组合问题

最近递归弄的人头疼,但是这两天看过来也稍微总结了一些不能称得上是技巧的技巧吧 问题如下,将1,2,3,4这四个数字排列组合的输出来,看网上有个很二的方法吧,就是将10000以内的数全部输出再筛选,对此有点无语,但是程序倒是挺好编的,嘿嘿 回归到正题中,用递归的思想解决 (1)采用旋转数字的方法,当步长为1时,1234还是1234,步长为2的时候,1234可以变为1243.1324...,步长为3 的时候,1234可以变为1423,诸如此类,最重要的能体现递归的就是将每次递归一次的数字还可以接着旋

LightOJ1005 Rooks(DP/排列组合)

题目是在n*n的棋盘上放k个车使其不互相攻击的方案数. 首先可以明确的是n*n最多只能合法地放n个车,即每一行都指派一个列去放车. dp[i][j]表示棋盘前i行总共放了j个车的方案数 dp[0][0]=1 转移就是从第i-1行转移到第i行,对于第i行要嘛放上一个车要嘛不放,放的话有n-j-1种方法.即dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*(n-j-1). 1 #include<cstdio> 2 #include<cstring> 3 using na