错位排列

问题: 十本不同的书放在书架上。现重新摆放,使每本书都不在原来放的位置。有几种摆法?
 
这个问题推广一下,就是错排问题,是组合数学中的问题之一。考虑一个有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\n",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%%\n",b);
21     }
22     return 0;
23 }

 

posted @ 2018-07-26 17:33  王陸  阅读(2980)  评论(0编辑  收藏  举报