超级传送门:
http://acm.hdu.edu.cn/showproblem.php?pid=2512
题目大意:
如{A,B,C}可以划分{{A},{B},{C}}, {{A,B},{C}}, {{B,C},{A}},
{{A,C},{B}},{{A,B,C}}。即一个集合可以划分为不同集合(1…n个)
给出一个数X,求出不同集合的种数。(结果%1000)
题目分析:
n元集合分划为 k 类的方案数记为 S(n,k),称为第二类 Stirling 数。
每个贝尔数都是"第二类Stirling数"的和。 B(n) = Sum(1,n) S(n,k).
题目就是要求这个B(n),所以我们要求出Stirling 数,(参考资料http://zhidao.baidu.com/question/87414823.html)
关于详细的解说,我学习了http://read.pudn.com/downloads65/sourcecode/math/232574/stir--%BC%AF%BA%CF%BB%AE%B7%D6%CE%CA%CC%E2/1/stir.ppt这个PPT
然后上面的分析给出了这种数的公式,下面代码实现就容易了:
注意:Bell数这个东西很大,第18个就已经超出了int了,幸好题目要求我们给出的答案%1000,不然要用大数做了
#include<iostream>
using namespace std;
const int N = 2001;
int data[N][N], B[N];
void NGetM(int m, int n)// m 个数 n个集合
{// data[i][j]:i 个数分成 j个集合
int min, i, j;
data[0][0] = 1; //
for( i = 1; i <= m; ++i ) data[i][0] = 0;
for( i = 0; i <= m; ++i ) data[i][i+1] = 0;
for( i = 1; i <= m; ++i ){
if( i < n ) min = i;
else min = n;
for( j = 1; j <= min; ++j ){
data[i][j] = (j*data[i-1][j] + data[i-1][j-1])%1000;
}
}
}
void compute(int m){// b[i]:Bell 数
NGetM(m, m);
memset(B, 0, sizeof(B));
int i, j;
for( i=1; i <= m; ++i )
for( j=0; j <= i; ++j ) B[i] += data[i][j];
}
int main()
{
int n,x;
while(scanf("%d",&n) != EOF)
{
while(n--)
{
scanf("%d",&x);
compute(x);
printf("%d\n",B[x]%1000);
}
}
return 0;
}