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\n",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) 整数向下取整