书架上面有n本书,我们打乱次序重新排,要求每本书重新排好之后位置要与之前不同,那么有多少种排放的方法。
首先,我们从规模较小的数据出发。
如果是一本书,那么不会出现错拍,则f[1] = 0;
如果是两本书,那么两两交换会出现一次错排,则f[2] = 1;
如果是三本书,那么错排次数是2,令原序是(1, 2, 3),则错排可以是(3, 1, 2),(2, 3, 1),即f[3] = 2;
………………………………
那么我们来推倒一下这个公式;
要求n个数的错排方法
那么首先,第一个数1有n-1种放法①(除去自身之外的位置都可以放)
然后我们来看剩下的n-1个数
那么这n-1个数有两种不同的选择
1.其中一个数选择之前数离开的位置k放置自身,那么这组数剩下的需要错排的数为n-2②,我们需要继续求n-2的错排数
2.不选择之前一个数离开的位置进行放置而重新选择另外一个位置进行放置,则位置k空了出来,且位置k属于n-1个数允许放置的范围,于是我们需要求n-1③个错排数
那么②+③错排数的情况总和则是当选择一个数之后数字错排的所有可能,可以随机选取n个数,则存在n-1种选择的方案
所以总的错排次数为①×(f②+f③),即f(n) = (n-1)*(f(n-1) + f(n-2))
PS:hdu2049是一道错排+组合数的题目,贴上AC代码用作参考
因为有n对新人,其中m个新郎认错,总可能次数为C(n, m)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
long long int cal(int n)
{
long long int ans = 1;
for (int i = 1; i <= n; i++)
ans *= i;
return ans;
}
int main()
{
int c, n, m;
scanf("%d", &c);
while(c--)
{
long long int a[22] = {0};
a[1] = 0;
a[2] = 1;
scanf("%d %d", &n, &m);
for (int i = 3; i <= m; i++)
a[i] = (i-1) * (a[i-1] + a[i-2]);
printf("%I64d\n", cal(n) / (cal(m) * cal(n - m)) * a[m]);
}
return 0;
}