ACM -- 算法小结(二)错排公式的应用

pala提出的问题: 十本不同的书放在书架上。现重新摆放,使每本书都不在原来放的位置。有几种摆法? 

这个问题推广一下,就是错排问题: n个有序的元素应有n!种不同的排列。如若一个排列式的所有的元素都不在原来的位置上,则称这个排列为错排

 

HDOJ RPG的错排

Problem Description
今年暑假杭电ACM集训队第一次组成女生队,其中有一队叫RPG,但做为集训队成员之一的野骆驼竟然不知道RPG三个人具体是谁谁。RPG给他机会让他猜猜,第一次猜:R是公主,P是草儿,G是月野兔;第二次猜:R是草儿,P是月野兔,G是公主;第三次猜:R是草儿,P是公主,G是月野兔;......可怜的野骆驼第六次终于把RPG分清楚了。由于RPG的带动,做ACM的女生越来越多,我们的野骆驼想都知道她们,可现在有N多人,他要猜的次数可就多了,为了不为难野骆驼,女生们只要求他答对一半或以上就算过关,请问有多少组答案能使他顺利过关。

Input

输入的数据里有多个case,每个case包括一个n,代表有几个女生,(n<=25), n = 0输入结束。

 

Sample Input

1

2

0

Sample Output

1

1
 
  • 解题思路:

  求0到n/2的所有错排和注意到对于i个人的错排的时候,还应该在错排前面乘上一个C(n,i)代表选取i个人进行错排

 1 #include<stdio.h>
 2 #include<math.h>
 3 #define e exp(1.0)
 4 __int64 ans[30];
 5 __int64 f[30];
 6 double p(int n)
 7 {
 8      int i;
 9      double res=1.0;
10      for(i=1;i<=n;i++)
11           res=res*i;
12      return res;
13 }
14 
15 __int64 C(int n,int r)
16 {
17      __int64 res=1;
18                 __int64 i;
19      for(i=1;i<=r;i++)
20      {
21           res=res*(n-i+1);
22           res=res/i;
23      }
24      return res;
25 }
26 void init()
27 {
28      int i,j;
29      for(i=1;i<=13;i++)
30           f[i]=(__int64)(p(i)/e+0.5);   //注释1
31      ans[1]=1;
32                 f[0]=1;
33 
34      for(i=2;i<=25;i++)
35      {
36           for(j=0;j<=i/2;j++)
37                ans[i]=ans[i]+f[j]*C(i,j);
38      }
39 }
40 
41 int main()
42 {
43      int n;
44      init();
45      while(scanf("%d",&n)!=EOF)
46      {
47           if(n==0)
48                break;
49           printf("%I64d
",ans[n]);
50      }
51      return 0;
52 }

 

注释1:错排问题,对于n个元素,对其重新排列,使得恰好有m个元素在原来的位置的排列总数P(n,m)

定理:令f(n) = P(n,0),则f(n) = (n-1)*f(n-1) + (n-1)*f(n-2)

定理:P(n,m) = (n!/m!)(1 - 1/1! + 1/2! -1/3! + …+ (-1)^(n-m) * 1/((n-m)!))

当m=0时,f(n) = P(n,0) = n! * (1 - 1/1! + 1/2! - 1/3! + … + ((-1)^n)/n! )

由级数知识化简为 f(n) = (n!/e +0.5) 整数向下取整