题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2068
这题用到了错排公式和排列组合的知识。
关于排列组合的内容我就不讲了,原谅我比较懒吧,真不会可以选择百度
什么是错排呢,错排就是n个人全都不在自己位置上的所有排列方法,或者说就是n个人完全排错的方法
1的错排数就是只有一个人,一个位置,不管怎么站,他永远都只能在自己的位置上,所以1的错排数为0;2的错排就是两个人都不在自己位置上,只有一种,所以2的错排数为1;3之后的自己慢慢画就清楚了。
递推错排公式:将n个人的错排数记为f[n]。将n中的第1个排错,假设放在第k个位置(k不在第一个,不然就不是错排了),有n-1种放法。那么第k个人若放在第1个位置,剩下的还有n-2个进行错排,为f[n-2];若第k个人不放在第1个位置,则还有n-1个需要错排,为f[n-1]。因此得到错排数公式:f[n]=(n-1)*(f[n-1]+f[n-2]),其中f[0]=0.f[1]=0,f[2]=1。
在本题中女生们只要求他答对一半或以上就算过关,所以错排j从0依次到n/2即可,j的错排数乘上n个选j个的组合数就是n个有j个排错的所有可能,最后累加就好了。下面是代码,有问题欢迎指出。
#include<iostream>
#include<cstdio>
using namespace std;
long long int res[30] = {0, 1, 1, 1};//最终结果
long long int b[15] = {1,0,1};//错排数
long long int factor[15] = {1, 1, 2, 6};//阶乘
long long int combination(int n, int m)//求 m 个中选 n 个的组合数
{
int minn = min (m - n, n);//m - n 和 n 较小的一个
long long int sum = 1;
//我怕 25 的阶乘太大,所以先算出来 25 的阶乘和两者中较大者的阶乘相除的结果
for (int i = 0; i < minn; i++)
{
sum *= (m - i);
}
sum /= factor[minn];
return sum;
}
void add()//求最终结果
{
for (int i = 3; i < 14; i++)
{
b[i] = (i - 1) * (b[i - 1] + b[i - 2]);//错排数
factor[i] = factor[i - 1] * i;//阶乘
}
for (int i = 4; i < 26; i++)
{
int j = i / 2;
long long int sum = 1;
for (int k = 2; k <= j; k++)
{
sum += combination(k, i) * b[k];
}
res[i] = sum;
}
return;
}
int main()
{
add();
int n;
while (cin >> n, n)
{
cout << res[n] << endl;
}
//喜欢C语言的朋友可以用下面这种,
/*
while (scanf ("%d", &n)!=EOF)
{
if (n == 0)
break;
printf("%lld\n", res[n]);
}
*/
return 0;
}