算法 - 集合划分问题

集合 X 的划分是 X 的非空子集的集合,使得每个 X 的元素 x 都只包含在这些子集的其中一个内。
等价的说,X 的子集的集合 P 是 X 的划分,如果:
(1) P 的元素都不是空集。(注:某些定义不需要这个要求)
(2) P 的元素的并集等于 X。(我们称 P 的元素覆盖 X。)
(3) P 的任何两个元素的交集为空。(我们称 P 的元素是两两不相交。)
例子: 集合 {1, 2, 3} 有五个划分。
{{1}, {2}, {3}}
{{1, 2}, {3}}
{{1, 3}, {2}}
{{1}, {2, 3}}
{{1, 2, 3}}
问题描述:
给定正整数 n 和 m,n, m ∈ N,计算 n 个元素组成的集合 {1, 2, . . . , n} 可以划分为多少个不同的由 m 个非空子集组成的集合,例:n=4,m=3 , 输出 6

算法思想:

可采用分治算法的思想求解,依次降低问题规模。
首先取出集合中的最后一个元素,该元素在划分后的集合中有两种情况:

  1. 该元素单独为一个子集,其余n-1个元素划分成另外的m-1个类(即递归回原问题求解)。
  2. 该元素作为m个子集中的某个子集的其中一个元素,一共有m个子集,因此这一步骤有m种情况,然后问题转变为将n-1个元素划分为m个子集。
    总结起来就是一个公式:T(n,m) = T(n-1,m-1) + m*T(n-1,m)

递归的终止情况:

  1. 当m=n时,即元素总数与集合数相等,T(n,m) = 1;
  2. 当m = 1或n = 1时,即只需划分成一个集合时,T(n,m) = 1;

C语言代码:

int calculate(int n,int m){
	if(m == n || n == 1 || m == 1){
		return 1;
	}
	return calculate(n - 1,m - 1) + m * calculate(n - 1,m);
}
int main(){
	int n = 4;
	int m = 3 ;
	printf("%d\n", calculate(n,m));
}

伪代码:

Calculate(n,m)
	if m == n || n == 1 || m == 1
		return 1
	return Calculate(n-1,m-1) + m*Calculate(n-1,m)

return Calculate(n,m)

参考资料:
https://blog.csdn.net/mikasa3/article/details/51283929
https://blog.csdn.net/pony_maggie/article/details/37729745

猜你喜欢

转载自blog.csdn.net/qq_41701363/article/details/88711031