题目
题目描述
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 }可以划分为多少个不同的非空子集。
输入数据
多组输入(<=10组数据,读入以EOF结尾) 每组一行输入一个数字,n(0<n<=18)
输出数据
每组输出一行结果。
样例输入
4
样例输出
15
思路讲解
元素个数n,集合数k,函数S(n,k)计算n个数组成k个子集有多少种情况。
首先,对于一个数,只有两种存在方式,要么自己单独成为一个子集,要么和别的数字一起组成一个子集。
接着,计算第一种存在方式能有多少子集时,只用考虑剩下n-1个数字组成k-1个子集有多少种情况,此时有S(n-1,k-1)种情况。
如果是第二种存在方式,那么这个数字需要被塞到n-1个数字组成的k个子集中,此时有S(n-1,k)种情况,而塞法又共有k种,所以第二种情况的总共k*S(n-1,k)种。
所以S(n,k)=S(n-1,k-1)+k*S(n-1,k)(n>k,k>0)
考虑边界条件,
k==0时,S(n,k)=0;
数字不能划分成0个集合,所以为0
n<k时,S(n,k)=0;
数字个数比要划分出的集合数还小,也为0
k==1或者k==n时,S(n,k)=1;
划分出一个集合就是全集,划分出n个集合时就是一个数一个集合,都是一种情况。
代码
#include <iostream> //集合划分
using namespace std;
#define LL long long
LL fun(int n, int k) //n个元素 分成k个子集 返回划分方案的个数
{
if (k == 1 || n == k)
return 1; //只有一种划分方法
else
return fun(n - 1, k - 1) + k * fun(n - 1, k); //递归公式
}
int main()
{
int n;
LL re;
while (cin >> n)
{
re = 0;
for (int i = 1; i <= n; i++) //从 1-n 划分
re += fun(n, i);
cout << re << endl;
}
return 0;
}