牛客_年会抽奖(错排问题)
年会抽奖__牛客网
解析代码
该题为经典的错排问题
用A、B、C……表示写着n位友人名字的信封,a、b、c……表示n份相应的写好的信纸。把错装的总数为记作 D(n)。假设把a错装进B里了,包含着这个错误的一切错装法分两类:
- b装入A里,这时每种错装的其余部分都与A、B、a、b无关,应有D(n-2)种错装法。
- b装入A、B之外的一个信封,这时的装信工作实际是把(除a之外的)n-1份信纸b、c……装入(除B以外 的)n-1个信封A、C……,显然这时装错的方法有D(n-1)种。
总之在a装入B的错误之下,共有错装法D(n-2)+D(n-1)种。 a装入C,装入D……的n-2种错误之下,同样都有D(n-1)+D(n-2)种错装法,因此D(n)=(n-1)[D(n-1)+D(n- 2)]
所以D(n) = (n-1) [D(n-2) + D(n-1)] 。特殊的,D(1) = 0, D(2) = 1.
错排的递推公式是:D(n) = (n - 1) [D(n - 2) + D(n - 1)],也就是n - 1倍的前两项和。公式推导可以参考百度百科。 通过这个递推公式可以得到在总数为n的时候,错排的可能性一共有多少种。
那么要求错排的概率,还需要另一个数值,就是当总数为n的时候,所有的排列组合一共有多少种,也就是n的阶乘。所以结果很简单,就是用公式求出第n项的错排种类,和n的阶乘,然后两者一除, 就是概率了。
#include <iostream>
using namespace std;
int main()
{
long long d[21] = { 0, 0, 1 }; // 错排数量,预留第一项为0
long long f[21] = { 1, 1, 2 }; // 阶乘
for (int i = 3; i <= 20; i++)
{
d[i] = (i - 1) * (d[i - 1] + d[i - 2]); //错排的递推公式
f[i] = i * f[i - 1]; // 阶乘的递推公式
}
int n = 0;
while (cin >> n)
{
printf("%.2f%%\n", 100.0 * d[n] / f[n]);
//用100.0来把结果处理成double,保留两位小数
}
return 0;
}