• 错位排列


    问题: 十本不同的书放在书架上。现重新摆放,使每本书都不在原来放的位置。有几种摆法?
     
    这个问题推广一下,就是错排问题,是组合数学中的问题之一。考虑一个有n个元素的排列,若一个排列中所有的元素都不在自己原来的位置上,那么这样的排列就称为原排列的一个错排。 n个元素的错排数记为D(n)。 研究一个排列错排个数的问题,叫做错排问题或称为更列问题。
     
    错排问题最早被尼古拉·伯努利和欧拉研究,因此历史上也称为伯努利-欧拉的装错信封的问题。这个问题有许多具体的版本,如在写信时将n封信装到n个不同的信封里,有多少种全部装错信封的情况?又比如四人各写一张贺年卡互相赠送,有多少种赠送方法?自己写的贺年卡不能送给自己,所以也是典型的错排问题。
     
     
     

    递推公式

    当n个编号元素放在n个编号位置,元素编号与位置编号各不对应的方法数用D(n)表示,那么D(n-1)就表示n-1个编号元素放在n-1个编号位置,各不对应的方法数,其它类推.
     
    第一步,把第n个元素放在一个位置,比如位置k,一共有n-1种方法;
    第二步,放编号为k的元素,这时有两种情况:
    ⑴把它放到位置n,那么,对于剩下的n-1个元素,由于第k个元素放到了位置n,剩下n-2个元素就有D(n-2)种方法;
    ⑵第k个元素不把它放到位置n,这时,对于这n-1个元素,有D(n-1)种方法;
    综上得到
     
    D(n) = (n-1) [D(n-2) + D(n-1)]
    特殊地,D(1) = 0, D(2) = 1.
    下面通过这个递推关系推导通项公式:
     
    为方便起见,设D(k) = k! N(k), k = 1, 2, …, n,
    则N(1) = 0, N(2) = 1/2.
    n ≥ 3时,n! N(n) = (n-1) (n-1)! N(n-1) + (n-1)! N(n-2)
    即 nN(n) = (n-1) N(n-1) + N(n-2)
    于是有N(n) - N(n-1) = - [N(n-1) - N(n-2)] / n = (-1/n) [-1/(n-1)] [-1/(n-2)]…(-1/3) [N(2) - N(1)] = (-1)^n / n!.
    因此
    N(n-1) - N(n-2) = (-1)^(n-1) / (n-1)!,
    N(2) - N(1) = (-1)^2 / 2!.
    相加,可得
    N(n) = (-1)^2/2! + … + (-1)^(n-1) / (n-1)! + (-1)^n/n!
    因此
    D(n) = n! [(-1)^2/2! + … + (-1)^(n-1)/(n-1)! + (-1)^n/n!].
    此即错排公式
     
    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    在这里我还看到了一种对递推公式其他的理解:
     
    一个人写了n封不同的信及相应的n个不同的信封,他把这n封信都装错了信封,问都装错信封的装法有多少种?

    分析:
    假设有信封A,B,C,D...;信件1,2,3,4...;全部装错有f(n)种情况。


    这里分两种情况考虑:①前面n-1个信封全部装错;②前面n-1个信封有一个没有装错其余全部装错。
    ①前面n-1个信封全部装错:因为前面n-1个已经全部装错了,所以第n封只需要与前面任一一个位置交换即可,总共有f(n-1)*(n-1)种情况。

    ②前面n-1个信封有一个没有装错其余全部装错:为什么考虑这种情况,因为n-1个信封中如果有一个没装错,那么我们把那个没装错的与n交换,即可得到一个全错位排列情况。
    得到这种情况的种数也很简单,即是忽略掉那个没装错的情况去排列其他的信封的全错排种数f(n-2)*(n-1)。
     
    综上,f(n)=f(n-1)*(n-1)+f(n-2)*(n-1)=(n-1)*(f(n-1)+f(n-2));
     
     
     
    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    例题:
    国庆期间,省城HZ刚刚举行了一场盛大的集体婚礼,为了使婚礼进行的丰富一些,司仪临时想出了有一个有意思的节目,叫做"考新郎",具体的操作是这样的:
      首先,给每位新娘打扮得几乎一模一样,并盖上大大的红盖头随机坐成一排; 然后,让各位新郎寻找自己的新娘.每人只准找一个,并且不允许多人找一个. 最后,揭开盖头,如果找错了对象就要当众跪搓衣板...
    看来做新郎也不是容易的事情...
    假设一共有N对新婚夫妇,其中有M个新郎找错了新娘,求发生这种情况一共有多少种可能.
    Input
    输入数据的第一行是一个整数C,表示测试实例的个数,然后是C行数据,每行包含两个整数N和M(1<M<=N<=20)。
    Output
    对于每个测试实例,请输出一共有多少种发生这种情况的可能,每个实例的输出占一行。
    Sample Input
    2
    2 2
    3 2
    Sample Output
    1
    3


    错排:有n对新婚夫妇,对应新娘在(1-n)n个位置上,让各位新郎寻找自己的新娘,全部找错共有多少种找法,记n个元素的错排总数为f(n)假设有n个新郎,第一个新郎可找  2-n   的任一个位置,共n-1种找法

    设第一个新郎找到了第 k 个位置,若此时第 k 个新郎正好找到了第 1 个位置,则只要将剩下的 n-2 错排,即f (n-2),就能实现全错排,若第k个新郎没有找到第1个位置,则将 n-1 个位置错排,即为f(n-1)

    由递推可得,f(n)=(n-1)*(f(n-1)+f(n-2))

     N个新郎找出M个新郎找错新娘,我们需要用到排列组合在 N 个新郎中选出是哪 M 个新娘,就是C(n,m)=n!/m!/(n-m)!

    用该组合数再去乘错位排列数就是该结果

     1 #include<cstdio>
     2 #include<algorithm>
     3 #define ll long long int
     4 using namespace std;
     5 ll f[30];
     6 ll c[30];
     7 int main()
     8 {
     9     ll n,m,t,i,ans;
    10     f[1]=0;
    11     f[2]=1;
    12     for(i=3;i<=30;i++)///错排
    13     {
    14         f[i]=(i-1)*(f[i-1]+f[i-2]);
    15     }
    16     c[0]=1;
    17     c[1]=1;
    18     for(i=2;i<=30;i++)///求前i项阶乘之和
    19     {
    20         c[i]=c[i-1]*i;
    21     }
    22     scanf("%lld",&t);
    23     while(t--)
    24     {
    25         scanf("%lld%lld",&n,&m);
    26         ans=c[n]/(c[m]*c[n-m]);
    27         printf("%lld
    ",ans*f[m]);
    28     }
    29 }

    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    Problem Description
    HDU 2006'10 ACM contest的颁奖晚会隆重开始了!
    为了活跃气氛,组织者举行了一个别开生面、奖品丰厚的抽奖活动,这个活动的具体要求是这样的:

    首先,所有参加晚会的人员都将一张写有自己名字的字条放入抽奖箱中;
    然后,待所有字条加入完毕,每人从箱中取一个字条;
    最后,如果取得的字条上写的就是自己的名字,那么“恭喜你,中奖了!”

    大家可以想象一下当时的气氛之热烈,毕竟中奖者的奖品是大家梦寐以求的Twins签名照呀!不过,正如所有试图设计的喜剧往往以悲剧结尾,这次抽奖活动最后竟然没有一个人中奖!
    我的神、上帝以及老天爷呀,怎么会这样呢?
    不过,先不要激动,现在问题来了,你能计算一下发生这种情况的概率吗?
    不会算?难道你也想以悲剧结尾?!
     
    Input
    输入数据的第一行是一个整数C,表示测试实例的个数,然后是C 行数据,每行包含一个整数n(1<n<=20),表示参加抽奖的人数。
    Output
    对于每个测试实例,请输出发生这种情况的百分比,每个实例的输出占一行, 结果保留两位小数(四舍五入),具体格式请参照sample output。
    Sample Input
    1 2
    Sample Output
    50.00%
     
     
    没有人能够获奖说明每个人都拿到的不是带有自己名字的字条,与信封问题一样是错位排列,求最后的概率等于错位排序的排列个数/所有可能的排列个数。
     1 #include<stdio.h>
     2 #include <stdlib.h>
     3 int main()
     4 {
     5     long long f[21]= {0};//定义不超过21的长整型数组用来存放错误的抽取
     6     f[1]=0;
     7     f[2]=1;
     8     for(int i=3; i<21; i++)
     9         f[i]=(i-1)*(f[i-1]+f[i-2]);//递推求值
    10     int C;
    11     scanf("%d",&C);
    12     while(C--)
    13     {
    14         int n;
    15         scanf("%d",&n);
    16         long long sum=1;
    17         for(int j=2; j<=n; j++)
    18             sum*=j;//求阶乘,表示的是所有的抽取方法
    19         double b=100.0*f[n]/sum;
    20         printf("%.2f%%
    ",b);
    21     }
    22     return 0;
    23 }
  • 相关阅读:
    详测 Generics Collections TDictionary(2): ContainsKey、ContainsValue、AddOrSetValue、TryGetValue
    详测 Generics Collections TQueue (3): OnNotify、Extract
    详测 Generics Collections TDictionary(4): OnKeyNotify、OnValueNotify
    详测 Generics Collections TStack(1): Push、Pop、Peek 其他功能同 TQueue
    详测 Generics Collections TDictionary(1): Add、Items、Keys、Values、Remove
    测试字符串写入类: TStringWriter
    在IIS 6 中使用HTTP压缩(Compression)遇到的一个问题
    [好消息]博客园新服务器到货
    [公告]美化家园行动—博客园成立Skin设计小组
    [公告]星期天上午更换新服务器
  • 原文地址:https://www.cnblogs.com/wkfvawl/p/9373204.html
Copyright © 2020-2023  润新知