【2-7】集合划分问题
问题描述:
n 个元素的集合{1,2,, n }可以划分为若干个非空子集。例如,当 n=4 时,集合{1,2,
3,4}可以划分为 15 个不同的非空子集如下:
{{1},{2},{3},{4}},
{{1,2},{3},{4}},
{{1,3},{2},{4}},
{{1,4},{2},{3}},
{{2,3},{1},{4}},
{{2,4},{1},{3}},
{{3,4},{1},{2}},
{{1,2},{3,4}},
{{1,3},{2,4}},
{{1,4},{2,3}},
{{1,2,3},{4}},
{{1,2,4},{3}},
{{1,3,4},{2}},
{{2,3,4},{1}},
{{1,2,3,4}}
´编程任务:
给定正整数 n,计算出 n 个元素的集合{1,2,, n }可以划分为多少个不同的非空子集。
´数据输入:
由文件 input.txt 提供输入数据。文件的第 1 行是元素个数 n。
´结果输出:
程序运行结束时,将计算出的不同的非空子集数输出到文件 output.txt 中。
输入文件示例 输出文件示例
input.txt output.txt
5 52
【题解】
Bell数的第i项就对应了1..i的非空集合划分个数。 可以这样递推 b[0]=b[1]=1(空或只有一个元素的话,那么只有一个划分,就是都把它们本身放在一个集合里) 否则 ```cpp for (int i = 2;i <= N;i++){ ll temp = 0; for (int j = 0;j < i;j++){ temp = temp + C(i-1,j)*bell[j];//在前i-1个数中挑出j个,它们和i不在一个集合中,其余的(i-1-j)个数都和i在同一个集合中. } bell[i] = temp; } ```【代码】
#include <cstdio>
#define ll long long
using namespace std;
const int N = 18;
int n;
ll f[N+10];
ll bell[N+10];
ll C(int n,int m){
if (n<m) return 0;
/*
n!/(n-m)!*m!
*/
ll temp1 = f[n]/f[n-m];
ll temp2 = f[m];
// printf("C(%d,%d)=%I64d\n",n,m,temp1/temp2);
return temp1/temp2;
}
int main(){
f[0] = 1;
for (int i = 1;i <= N;i++) f[i] = 1LL*f[i-1]*i;
bell[0] = 1;bell[1] = 1;
for (int i = 2;i <= N;i++){
ll temp = 0;
for (int j = 0;j < i;j++){
temp = temp + C(i-1,j)*bell[j];
}
bell[i] = temp;
}
int n;
scanf("%d",&n);
printf("%I64d\n",bell[n]);
return 0;
}