题目链接:
http://acm.hdu.edu.cn/showproblem.php?
题目大意:
有N个房间,每一个房间的要是随机放在某个房间内,概率同样。有K次炸门的机会。
求能打开全部房间门,进入全部房间的概率有多大。
解题思路:
门和钥匙的相应关系出现环。打开一个门后,环内的门都能够打开。
也就意味着:
N个房间的钥匙与门形成1~K个环的概率有多大。
也就是求N个元素。构成K个以内的环,且1不成自环的概率。
N个元素形成K个环的方法数是第一类stirling数 S(N。K)。
N个元素形成K个环。且1成自环的方法数是S(N-1,K-1)。
则N个元素形成K个环。且1不成自环的方法数是S(N。K) - S(N-1,K-1)。
要是随机放的总的方法数为N!。
则概率P(N,K)为( S(N,K) - S(N-1,K-1) + S(N。K-1) - S(N-1。K-2) + … +
S(N,1) - S(N。0) ) / N!
AC代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define LL long long using namespace std; LL F[25],Stirling[25][25]; void Solve() { F[0] = 1; for(int i = 1; i <= 20; ++i) //阶乘数组 F[i] = i*F[i-1]; for(int i = 1; i <= 20; ++i) //算出第一类stirling数 Stirling[i][0] = 0; Stirling[1][1] = 1; for(int i = 1; i <= 20; ++i) { for(int j = 1; j <= i; ++j) { if(i == j) Stirling[i][j] = 1; else Stirling[i][j] = Stirling[i-1][j-1] + (i-1)*Stirling[i-1][j]; } } for(int i = 1; i <= 20; ++i) //取绝对值 { for(int j = 1; j <= 20; ++j) { Stirling[i][j] = abs(Stirling[i][j]); } } } int main() { Solve(); int T,N,K; scanf("%d",&T); while(T--) { scanf("%d %d",&N,&K); LL sum = 0; for(int i = 1; i <= K; ++i) sum += Stirling[N][i] - Stirling[N-1][i-1]; printf("%.4f\n",(double)sum / (double)F[N]); //.4lf输出不了正确结果 } return 0; }