复习离散时看见这个问题就想着写一下
思路:
将问题转化为再原有划分的基础上,增加一个元素,有多少种可能。将n个元素划分成m个元素,可以转化为,由n-1和m-1的划分加上一个单独的元素,或n-1和m的划分,这时可以理解成,将新添加的元素加到m个集合中的一个。
贴一个newbing的解释:
5个元素的集合共有不同的划分数为 52 。这个问题可以转化为将一个5元素集合划分成1个或多个非空子集的问题。我们可以使用斯特林数来计算这个问题的答案。斯特林数是将n个不同的元素划分成k个非空集合的方案数,记作S(n,k)。对于这个问题,我们需要计算S(5,1)+S(5,2)+S(5,3)+S(5,4)+S(5,5)的值,其中S(n,k)的值可以使用递推公式或者递归公式来计算。根据斯特林数的定义,我们可以得到:
S(n,k)=S(n-1,k-1)+(n-1)*S(n-1,k)
其中,当k=0或n<k时,S(n,k)=0;当k=1时,S(n,k)=1。
因此,我们可以得到:
S(5,1)=1
S(5,2)=6
S(5,3)=25
S(5,4)=50
S(5,5)=24
因此,5个元素的集合共有不同的划分数为:
S(5,1)+S(5,2)+S(5,3)+S(5,4)+S(5,5)=1+6+25+50+24=52
代码:
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
map<pair<long long, long long>, long long> dp;
long long F(long long n, long long m)
{
if (m == 1)
{
return 1;
}
if (m == n)
{
return 1;
}
if (dp.find({ n,m }) != dp.end())
{
return dp[{n, m}];
}
dp[{n, m}] = F(n - 1, m - 1) + m * F(n - 1, m);
return dp[{n, m}];
}
int main()
{
long long n;
cin >> n;
long long ans = 0;
for (int i = 1; i <= n; i++)
{
ans += F(n, i);
}
cout << ans << endl;
return 0;
}