集合的划分《信息学奥赛一本通-动态规划》

本文探讨了如何使用递归和动态规划的方法解决将n个不同元素分配到k个无空集的盒子中的划分计数问题S(n,k),通过理解两种思考方式并结合具体边界条件和转移方程,提供了代码实现。

【题目描述】

设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;		
}

第二种就是书上的思路啦,大家学会第一种考虑方式再想第二种肯定不难~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值