mthoutai

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

题目链接:

http://acm.hdu.edu.cn/showproblem.php?

pid=3625


题目大意:

有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;
}




posted on 2017-06-11 15:35  mthoutai  阅读(228)  评论(0编辑  收藏  举报