不容易系列之(4)――考新郎
小X特别喜欢吃哈密瓜,而且小X还要把所有的哈密瓜按照大小排列放在超级无敌大冰箱里,从小到大吃。小X的好兄弟小Y特别喜欢捉弄小X,他故意改变一些哈密瓜的位置,把小X的哈密瓜顺序打乱惹小X生气。
假设小X一共有N个哈密瓜,小Y要改变M个哈密瓜的位置,问小Y有多少种打乱哈密瓜的方式。
input
输入数据的第一行是一个整数C,表示测试实例的个数,然后是C行数据,每行包含两个整数N和M(1<M<=N<=20)。
Output
对于每个测试实例,请输出一共有多少种发生这种情况的可能,每个实例的输出占一行。
Sample Input
2
2 2
3 2
Sample Output
1
3
题解:我们完成题目要求分为两步,第一步是从n个哈密瓜取m个的排列数。
第二步是全错位排列数。
全错位排列被著名数学家欧拉(Leonhard Euler,1707-1783)称为“组合数论的一个妙题”的“装错信封问题”的两个特例。
一个人写了n封不同的信及相应的n个不同的信封,他把这n封信都装错了信封,问都装错信封的装法有多少种?
可以看出我们需要在N个新郎找出M个哈密瓜放错位置,所以需要N的全排列除以M的全排列然后乘以M个哈密瓜都放错的种数,设这是一种函数关系为f,那么会发现从f(4)开始,f[i]=(f[i-1]+f[i-2])*(i-1),所以我们只需算出f(2),f(3)即可。
研究错排问题的方法
枚举法
对于情况较少的排列,可以使用枚举法。
当n=1时,全排列只有一种,不是错排,D1= 0。
当n=2时,全排列有两种,即1、2和2、1,后者是错排,D2= 1。
当n=3时,全排列有六种,即1、2、3;1、3、2;2、1、3;2、3、1;3、1、2;3、2、1,其中只有有3、1、2和2、3、1是错排,D3=2。用同样的方法可以知道D4=9。
最小的几个错排数是:D1= 0,D2= 1,D3=2,D4= 9,D5= 44,D6= 265,D7= 1854.。
提交代码
#include<stdio.h>
int main()
{
long long a[22]={0},sum,sum1;
int m,n,i,j,t;
a[2]=1,a[3]=2;
for(i=4;i<=20;i++)
a[i]=(a[i-1]+a[i-2])*(i-1);
scanf("%d",&m);
while(m--)
{
sum=1,sum1=1;
scanf("%d %d",&i,&j);
if(i==j)
printf("%lld\n",a[i]);
else if(i==j+1){
sum=a[j]*i;
printf("%lld\n",sum);
}
else{
for(n=j;n>0;n--,i--){
sum*=i;
}
for(n=j;n>1;n--)
sum1*=n;
sum=sum/sum1;
sum*=a[j];
printf("%lld\n",sum);
}
}
return 0;
}