先和第二类做一个对比
第一类Stirling数是有正负的,其绝对值是包含n个元素的集合分作k个环排列的方法数目。
递推公式为, S(n,0) = 0, S(1,1) = 1. S(n+1,k) = S(n,k-1) + nS(n,k)。
边界条件: S(0 , 0) = 1 S(p , 0) = 0 p>=1 S(p , p) =1 p>=0
一些性质: S(p ,1) = 1 p>=1 S(p, 2) = 2^(p-1)– 1 p>=2
第二类Stirling数是把包含n个元素的集合划分为正好k个非空子集的方法的数目。
递推公式为: S(n,k)=0; (n<k||k=0) S(n,n) = S(n,1) = 1,
S(n,k) = S(n-1,k-1) + kS(n-1,k).
考虑第p个物品,p可以单独构成一个非空集合,此时前p-1个物品构成k-1个非空的
不可辨别的集合,方法数为S(p-1,k-1);
也可以前p-1种物品构成k个非空的不可辨别的集合,
第p个物品放入任意一个中,这样有k*S(p-1,k)种方法。
然后说下这道题目,题目说每个房间都锁死了,切钥匙都在房间里面。我们可以任意的强行开一个门(除了1号门),然后拿到钥匙去开其他门,重复这个操作,如果开不了门,我们在强行开一个,依次类推。题目要求最多只允许强开k扇门的时候,有多大的几率把所有的门都打开。模拟一下发现和第一类斯特林数差不多,就是要特殊处理一下第一个门不能强开的情况。
ac代码:
#include <iostream> #include <stdio.h> #include <string> #include <string.h> #include <algorithm> using namespace std; typedef long long ll; ll stir[22][22]; ll jie[22]; void init() { stir[1][0]=0; stir[1][1]=1; for(int i=2;i<=20;i++) for(int j=1;j<=i;j++) stir[i][j]=stir[i-1][j-1]+(i-1)*stir[i-1][j]; jie[1]=1; for(int i=2;i<=20;i++)jie[i]=jie[i-1]*i; } int main() { int T; scanf("%d",&T); init(); while(T--) { int n,k; scanf("%d%d",&n,&k); ll ans=0; for(int i=1;i<=k;i++) ans+=stir[n][i]-stir[n-1][i-1]; printf("%.4lf ",ans*1.0/jie[n]); } return 0; }