该程序用于将具有n个元素的集合的分区数计数为k个子集,我在这里return k*countP(n-1, k) + countP(n-1, k-1);
感到困惑
有人可以解释一下这里发生了什么吗?
为什么我们要乘以k?
注意->我知道这不是计算将成为DP的分区数量的最佳方法
// A C++ program to count number of partitions
// of a set with n elements into k subsets
#include<iostream>
using namespace std;
// Returns count of different partitions of n
// elements in k subsets
int countP(int n, int k)
{
// Base cases
if (n == 0 || k == 0 || k > n)
return 0;
if (k == 1 || k == n)
return 1;
// S(n+1, k) = k*S(n, k) + S(n, k-1)
return k*countP(n-1, k) + countP(n-1, k-1);
}
// Driver program
int main()
{
cout << countP(3, 2);
return 0;
}
答案 0 :(得分:4)
您提到的是Stirling numbers of the second kind,它列举了将n个对象集合划分为k个非空子集并用或表示的方法的数量。
它的递归关系是:
k > 0
的初始条件:
。
使用动态编程来计算它比使用递归方法更快:
int secondKindStirlingNumber(int n, int k) {
int sf[n + 1][n + 1];
for (int i = 0; i < k; i++) {
sf[i][i] = 1;
}
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < k + 1; j++) {
sf[i][j] = j * sf[i - 1][j] + sf[i - 1][j - 1];
}
}
return sf[n][k];
}
答案 1 :(得分:3)
每个countP
调用都隐式考虑了集合中的单个元素,将其称为 A 。
countP(n-1, k-1)
一词来自 A 本身在集合中的情况。在这种情况下,我们只需要计算将所有其他元素(N-1)划分为(K-1)个子集有多少种方法,因为 A 本身占据了一个子集。
k*countP(n-1, k)
术语来自于 A 在其自身集合中 not 的情况。因此,我们找出了将所有其他(N-1)个值划分为K个子集并乘以K的方法的数量,因为有K个可能的子集,我们可以添加 A 。
例如,将集合[A,B,C,D]
与K=2
一起考虑。
第一种情况countP(n-1, k-1)
描述了以下情况:
{A, BCD}
第二种情况k*countP(n-1, k)
描述了以下情况:
2*({BC,D}, {BD,C}, {B,CD})
或者:
{ABC,D}, {ABD,C}, {AB,CD}, {BC,AD}, {BD,AC}, {B,ACD}
答案 2 :(得分:2)
我们如何获得countP(n,k)
?假设我们已经将先前的n-1
元素划分为一定数量的分区,现在我们有了第n个元素,并且尝试对k
进行分区。
为此,我们有两个选择:
任一
n-1
元素划分为k
个部分(我们有countP(n-1, k)
种方式),并将第n个元素放入这些部分之一(我们有k
个选择)。因此,我们有了k*countP(n-1, k)
。或:
n-1
元素划分为k-1
分区(我们有countP(n-1, k-1);
的方式),并且将第n个元素作为单个部分来实现{{1 }}分区(我们只有一种选择:将其分开放置)。因此,我们有了k
。 所以我们将它们总结并得到结果。
答案 3 :(得分:2)
在This的基础上,集合的分区是将集合的元素分组为非空子集的方式,每个元素都包含在一个子集中,并且只有一个子集中。因此,n个元素集的分区总数为Bell number,其计算方式如下: Bell number formula 因此,如果要将公式转换为递归函数,它将类似于: k * countP(n-1,k)+ countP(n-1,k-1);