问题描述:
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}}
其中,集合{{1,2,3,4}} 由1个子集组成;集合{{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}} 由2个子集组成;集合{{1,2},{3},{4}},{{1,3},{2},{4}},{{1,4}.{2},{3}},{{2,3},{1},{4}},{{2,4},{1},{3}},{{3,4},{1},{2}} 由3 个子集组成;集合{{1},{2},{3},{4}} 由4个子集组成。
编程任务:
给定正整数n 和m,计算出n 个元素的集合{1,2,., n }可以划分为多少个不同的由m 个
非空子集组成的集合。
数据输入:
由文件input.txt 提供输入数据。文件的第1 行是元素个数n 和非空子集数m。
结果输出:
程序运行结束时,将计算出的不同的由m个非空子集组成的集合数输出到文件output.txt中。
输入文件示例 输出文件示例
4 3 6
算法思想
这是一个很简单的分治题,我们可以定义一个划分函数setSplit(n,m),其中n表示划分集合规模,m表示要划分的子集数目,我们可以这样处理,从第n个数开始往1推导,对于第i个数i,有2种处理方法:
- i 可以进入m个集合中的任何一个:
m*setSplit(n-1,m)
- i 可以独自成为一个单数集:
setSplit(n-1,m-1)
- 当n=1或者m=n时只存在一种划分
- 当n<m时划分数为0
从而得到递推关系式 - 另外由于计算过程中会有重复的计算,所以我们用记忆型递推关系,将setSplit(n,m)存放在数组中,从而避免重复计算,提升算法效率
代码如下:
#include <iostream>
using namespace std;
const int maxn=1005;
int table[maxn][maxn]={0};
int setSplit(int n,int m)
{
if(n<m) return 0;
if(n==m||m==1) return 1;
if(!table[n][m]) table[n][m]=m*setSplit(n-1,m)+setSplit(n-1,m-1);
//加入任意一个集合或者独立成一个集合
return table[n][m];
}
int main()
{
int n,m;
cin>>n>>m;
cout<<setSplit(n,m)<<endl;
return 0;
}
算法复杂度分析
(1)由于递推过程中我们从最底层开始推导,所以算法最终会将一个mn的表格填满,算法复杂度为O(mn)