Stirling数。。

第一类 Stirling数。。

S(n, k) 存在正负。。其绝对值表示将n个数分为k个环排列的数目。。
递推公式
S(n, 0) = 0; n >= 1;
S(n, n) = 1; n >= 0;
s(n, k) = s(n - 1, k - 1) + (n - 1) * s(n - 1, k) , 1 <= k <= n - 1 //第k个数自成环,前n - 1个成环 + 将第个k数插入到前n - 1成环序列中

hdu 3625
题目大意:n个房间,每个房间都放有一把钥匙,1号钥匙对应1号门。。所有门都关着。。。计算最少的破门数(一号门不能破)
计算环排列数。。减去1号钥匙对应1号门的环排列数。。。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <stack>
#include <queue>
#include <map>
#include <cctype>
#include <ctime>
#define MAX 0x3f3f3f3f
#define LL __int64
#define MOD (100)
#define N (20 + 1)

using namespace std;

LL s[N][N], fn[N];

void init()
{
    memset(s, 0, sizeof(s));
    for (int i = 1; i < N; i++)
    {
        s[i][i] = 1;
    }

    for (int i = 2; i < N; i++)
    {
        for (int j = 1; j <= i - 1; j++)
        {
            s[i][j] = s[i - 1][j - 1] + (i - 1) * s[i - 1][j];
        }
    }

    fn[1] = fn[0] = 1;
    for (int i = 2; i < N; i++)
    {
        fn[i] = i * fn[i - 1];
    }


}

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 += s[n][i] - s[n - 1][i - 1];
        }

        printf("%.4lf\n", 1.0 * ans / fn[n]);        
    }
}

hdu 4372
题目大意:有N座房子。。高度分别为1~N。。向前看能看到F座。。向后看能看到B座。。。求排列数。。。

第二类 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) + k * S(n - 1, k);

例:将n个有区别的球放入k个无标号的盒子中(n >= k >= 1, 且盒子不允许为空)的方案数为Stirling数。。。
推导:设有n个不同的球,分别为b1, b2, …bn, 从中取出一个球bn, bn的方法有两种。。。
1. bn 独占一个盒子, 剩下的球放在k-1个盒子里。。。方案数为S(n - 1, k - 1);
2. bn与别的球占同一个盒子。。方案数为k * S(n - 1, k);

LL s[N+2][N+2] = {1}, bell[N+2];

void init()
{
    for (int i = 1; i <= N; i++)
    {
        s[i][0] = 0;  
        s[i][i] = 1;  
        for(int j = 1; j < i; j++)  
        {
            s[i][j] = (s[i-1][j-1] + j * s[i-1][j]) % MOD;  
        }
    }

    for (int i = 1; i <= N; i++)
    {
        bell[i] = 0;
        for (int j = 0; j <= i; j++)
        {
            bell[i] = (bell[i] + s[i][j]) % MOD;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值