【题目描述】
设S是一个具有n个元素的集合,S=〈a1,a2,……,an〉,现将S划分成k个满足下列条件的子集合S1,S2,……,Sk,且满足:
1.Si≠∅
2.Si∩Sj=∅ (1≤i,j≤k,i≠j)
3.S1∪S2∪S3∪…∪Sk=S
则称S1,S2,……,Sk是集合S的一个划分。它相当于把S集合中的n个元素a1,a2,……,an 放入k个(0<k≤n<30)无标号的盒子中,使得没有一个盒子为空。请你确定n个元素a1,a2,……,an放入k个无标号盒子中去的划分数S(n,k)。
【输入】
给出nn和kk。
【输出】
n个元素a1,a2,……,an放入k个无标号盒子中去的划分数S(n,k)。
【输入样例】
10 6
【输出样例】
22827
书上题解用的是递归解法,但是我转念一想考虑是不是可以用动归写法a这个题,若是做过《一本通》上的分苹果,很容易注意到两道题目是比较相似的,但是此题比分苹果那题稍微容易一点,因为状态转移方程比较好想
和分苹果不同之处在于:分集合这道题是不能有空集的,也就是说确保有每个集合都至少要有一个元素,所以依据这点可以考虑到两个临界情况,一是当集合数目大于元素数目的时候种类数是一定为0的,二是当集合数目恰好等于元素个数的时候种类数恰好为1,还有一种临界条件自然很容易想到:当集合数目为1时,自然若元素个数不为0,则不论元素个数是多少,方案书目恒为1。
此题和分苹果分析的思路接近,若从简单状况开始分析则比较容易得到转移关系,但是特别要注意一点,分集合不是分苹果,每个元素是不一样的!
我们其实有两种思考方式:一种是以集合为基准思考,集合个数不变,元素多出一个怎么分配
还有一种思考方式就是以元素为基准,元素个数不变,集合多出一个怎么分配
若是第一种思考方式,那么前提自然是元素个数要大于等于集合个数,没多出一个元素自然放在哪个集合都可,所以每多出一个元素,累加的方案数其实就是集合的个数,但是这还不够,还不能概括所有的情况。比如说若是把4个元素分到3个集合中,那么照此分配方式自然就只有3*f[3][3],而f[3][3]自然只有一种,所以一共就只有3种方案?显然不对,f[3][4]不仅只能由f[3][3]转移,也可以由f[2][3]转移,区别就是:f[3][3]转移至f[4][3]说明在新加入元素之前三个集合就已经各有一共元素了,但是这不太合逻辑,难道我们在加入最后一个集合之前所有集合必须非空吗?显然没有这种规定,所以我们实际上是忽略了之前元素没有把集合全部占满的情况,但是有人会问了,我们考虑的不是新加入一个元素的情况吗?为什么集合个数还会在转移方程中变化?其实并不是集合个数变化了,因为按照题目规定,空集是不能被算作一个集合的,所以看起来集合个数变多了,实际上只是新加入的元素填了一个空集而已。
但是要注意先写出所有临界条件才不会出错
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N = 110;
ll f[N][N];
int n,m;
int main()
{
cin>>n>>m;
for(int i=1;i<=100;i++) f[1][i] = 1;
for(int i=1;i<=100;i++) f[i][i] = 1;
for(int i=2;i<=m;i++)
for(int j=i;j<=n;j++)
f[i][j] = i*f[i][j-1]+f[i-1][j-1];
cout<<f[m][n];
return 0;
}
第二种就是书上的思路啦,大家学会第一种考虑方式再想第二种肯定不难~
本文探讨了如何使用递归和动态规划的方法解决将n个不同元素分配到k个无空集的盒子中的划分计数问题S(n,k),通过理解两种思考方式并结合具体边界条件和转移方程,提供了代码实现。
9979

被折叠的 条评论
为什么被折叠?



