19180 集合划分问题
   
    时间限制:1000MS 代码长度限制:10KB
    
    提交次数:0 通过次数:0
   
     
   
题型: 编程题 语言: G++;GCC;VC;JAVA
    
    
    Description
   
    教材课后习题2-8
    
    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(1<=n<=20),计算出n个元素的集合{1,2,…,n} 可以化为多少个由m个不同的非空子集组成的集合。
   
    
    
    输入格式
   
两个整数n和m。
    
    
    输出格式
   
    集合划分的数量。
    
    注意此类问题数据较大,需要使用long long 类型。
   
    
    
    输入样例
   
4 3
    
    
    输出样例
   
6
    
    
    解题思路
   
    
    
    递归
   
    
    
    找规律
   
递归思路:以最大数n为例
- 
     n
 
 独立作为一个子集
 
 ,问题就演变为 n – 1 个数分为 m – 1 个集合。即:如果现在是将4个元素划分为3个集合,如果加入第四个数时要求为独立一个子集,那在这基础上,结果加上个数为3个元素划分为2个集合的数量。
- n 和其他数字在一起构成子集,那么先将 n – 1 个数字分为 m 个集合,再将 n 插入到这 m 个集合中的某个集合中去,因此此时会有 m 种插入方案。
    
    
    举例
   
以集合元素个数为4为例,我们来看应该怎么求解集合划分问题。因为我们采用的是分治策略,因此,我们先看集合元素个数为3的集合的划分
考虑3个元素的集合,可划分为
- 
     1个子集的集合:{
 
 {1,2,3}}
- 
     2个子集的集合:{
 
 {1,2},{3}},{
 
 {1,3},{2}},{
 
 {2,3},{1}}
- 
     3个子集的集合:{
 
 {1},{2},{3}}
因此 F(3, 1) = 1; F(3, 2) = 3; F(3, 3) = 1;
如果我们要求4个元素的集合划分为2个子集的集合的个数F(4,2),求解过程如下:
- 
     如果这第4个元素不是独立子集,因此在2个子集的集合中加入4,加入4的方式如下:
 
 {
 
 {1,2,4},{3}},{
 
 {1,2},{3,4}},
 
 {
 
 {1,3,4},{2}},{
 
 {1,3},{2,4}},
 
 {
 
 {2,3,4},{1}},{
 
 {2,3},{1,4}}
即如果有 m 个子集,那么该情况的个数为 m * F(n – 1, m)
- 
     如果这第4个元素作为独立子集,只有一种方式
 
 {
 
 {1,2,3},{4}}
即在 F(n – 1, m – 1) * 1
    
     所以:F(4, 2)= 2 * F(3, 2)+ F(3, 1)
    
由以上的演示可以得出集合划分的公式如下:
    
    
    此式中n为元素个数,m为子集个数。
   
    
    
    递归终止条件
   
- 可划分为的集合数为1时,即不能再少了;
- 当 n == m,即元素和集合个数相等时,比如6个元素划分为4个,只能递归至4个元素划分为4个集合,而不能继续递归成3个元素划分为4个集合了(此时会出现空集合,而题目要保证每个集合至少一个元素)。
    
    
    算法思路
   
更多注释可查看下方的完整代码中,有助于理解
    
    
    代码如下
   
#include <iostream>
using namespace std;
/*
20 10
5917584964655
*/
long long f(int n,int m)
{
    // 递归结束条件,n和m相等,即此时每个集合只有一个元素,因为比如4个元素最多只能划分为4个集合,或只划分为1个集合
    if(n == m || m == 1) {
        return 1;
    } else {
        return f(n - 1, m - 1) + m * f(n - 1, m);
    }
}
int main()
{
    int n, m;
    cin >> n >> m;
    cout << f(n, m) << endl;
    return 0;
}
    
    
    最后
   
对我感兴趣的小伙伴可查看以下链接
- 
     我的掘金主页:
 
 https://juejin.cn/user/1302297507801358
 
- 
     博客主页:
 
 http://blog.zhangjiancong.top/
 
- 公众号:Smooth前端成长记录
 
