错位排列
问题: 十本不同的书放在书架上。现重新摆放,使每本书都不在原来放的位置。有几种摆法?
这个问题推广一下,就是错排问题,是组合数学中的问题之一。考虑一个有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)。
分析:
假设有信封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个新郎找错了新娘,求发生这种情况一共有多少种可能.
首先,给每位新娘打扮得几乎一模一样,并盖上大大的红盖头随机坐成一排; 然后,让各位新郎寻找自己的新娘.每人只准找一个,并且不允许多人找一个. 最后,揭开盖头,如果找错了对象就要当众跪搓衣板...
看来做新郎也不是容易的事情...
假设一共有N对新婚夫妇,其中有M个新郎找错了新娘,求发生这种情况一共有多少种可能.
Input
输入数据的第一行是一个整数C,表示测试实例的个数,然后是C行数据,每行包含两个整数N和M(1<M<=N<=20)。
Output
Output
对于每个测试实例,请输出一共有多少种发生这种情况的可能,每个实例的输出占一行。
Sample Input
Sample Input
2 2 2 3 2Sample 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\n",ans*f[m]); 28 } 29 }
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Problem Description
HDU 2006'10 ACM contest的颁奖晚会隆重开始了!
为了活跃气氛,组织者举行了一个别开生面、奖品丰厚的抽奖活动,这个活动的具体要求是这样的:
首先,所有参加晚会的人员都将一张写有自己名字的字条放入抽奖箱中;
然后,待所有字条加入完毕,每人从箱中取一个字条;
最后,如果取得的字条上写的就是自己的名字,那么“恭喜你,中奖了!”
大家可以想象一下当时的气氛之热烈,毕竟中奖者的奖品是大家梦寐以求的Twins签名照呀!不过,正如所有试图设计的喜剧往往以悲剧结尾,这次抽奖活动最后竟然没有一个人中奖!
我的神、上帝以及老天爷呀,怎么会这样呢?
不过,先不要激动,现在问题来了,你能计算一下发生这种情况的概率吗?
不会算?难道你也想以悲剧结尾?!
为了活跃气氛,组织者举行了一个别开生面、奖品丰厚的抽奖活动,这个活动的具体要求是这样的:
首先,所有参加晚会的人员都将一张写有自己名字的字条放入抽奖箱中;
然后,待所有字条加入完毕,每人从箱中取一个字条;
最后,如果取得的字条上写的就是自己的名字,那么“恭喜你,中奖了!”
大家可以想象一下当时的气氛之热烈,毕竟中奖者的奖品是大家梦寐以求的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%%\n",b); 21 } 22 return 0; 23 }