题目描述
今年公司年会的奖品特别给力,但获奖的规矩却很奇葩:
- 首先,所有人员都将一张写有自己名字的字条放入抽奖箱中;
- 待所有字条加入完毕,每人从箱中取一个字条;
- 如果抽到的字条上写的就是自己的名字,那么“恭喜你,中奖了!”
现在告诉你参加晚会的人数,请你计算有多少概率会出现无人获奖?
输入描述:
输入包含多组数据,每组数据包含一个正整数n(2≤n≤20
)。
输出描述:
对应每一组数据,以“xx.xx%”
的格式输出发生无人获奖的概率。
输入例子:
2
输出例子:
50.00%
解这道题,我们需要明白什么时候才算做都不获奖
?全部都不获奖的概率如何计算
?
对于什么时候才算做都不获奖
,当然是所有人都拿到了别人的名字,没有拿到自己的名字。
全部都不获奖的概率
必定是由 n个人都拿错的情况种数
除 n个人拿出的所有排列情况数
。
n个人拿出的所有排列情况数
显然是n的阶乘。
n个人都拿错的情况种数
与上一道题 PAT乙级(Basic Level)练习题 发邮件 是一样的。
假设a
的名字没有被a
拿到,其他n - 1
个人都有可能拿到,即有n - 1
种情况。假设b
拿到了a
的名字,那么对于b的名字有两种情况,
第一种是b
的名字被a
拿到了,也就是a
、b
互相拿到了对方的名字,那么对于其他n - 2
个人互相拿错又是一个子问题f(n - 2)
.
第二种是b
的名字没有被a
拿到,则剩下的问题是子问题f(n - 1).
因此可得递推公式f(n) = (n - 1) * (f(n - 1) + f(n - 2))
.
最终得出公式n人都不获奖的概率h(n) = (n - 1) * (f(n - 1) + f(n - 2)) / (n!)
.
#include <iostream>
#include <math.h>
using namespace std;
int main(int argc, const char * argv[]) {
int n = 0;
//fTable[n]记录n个人都拿错(全不获奖)的情况种数,allTable[n]记录所有可能的组合情况n的阶乘
long long fTable[21] = {0, 0, 1}, allTable[21] = {0, 1, 2};
for (int i = 3; i < 21; ++i) {
//递推计算i个人全部拿错
fTable[i] = (i - 1) * (fTable[i - 1] + fTable[i - 2]);
//递推计算i的阶乘
allTable[i] = i * allTable[i - 1];
}
//scanf返回值为正确输入数据的变量个数,当一个变量都没有成功获取数据时,此时返回-1
while (scanf("%d", &n) != - 1) {
//注意%属于控制字符,两个连续的%%才表示真正的%字符
printf("%4.2lf%%\n", 100 * double(fTable[n]) / allTable[n]);
}
return 0;
}