ybt1315 递归例题(虽然用的是递推)集合划分
ybt1315 集合划分
递归算法
【题目描述】
设S是一个具有n个元素的集合,S=⟨a1,a2,……,an⟩,现将S划分成k个满足下列条件的子集合S1,S2,……,Sk,且满足:
1.Si≠∅
2.Si∩Sj=∅ (1≤i,j≤k,i≠j1≤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 放入kk个无标号盒子中去的划分数S(n,k)。
【输入】
给出n和k。
【输出】
n个元素a1,a2,……,an 放入k个无标号盒子中去的划分数S(n,k)。
【输入样例】
10 6
【输出样例】
22827
设fi,j为i个元素时分成j个集合的方案数。
对任意的n,当满足k=n的时候,则fn,k=1。
分析除特殊情况外的一般情况f4,,3=6,则是由:
方案一:1,2;3;4
方案二:1;2,3;4
方案三:1;2;3,4
方案四:1,3;2;4
方案五:1,4;2;3
方案六:2,4;1;3
组成的。
可以将其理解为f3,3的情况往里加了元素4。
这样4就有两种情况,自己成为独立的集合(方案一,二,四);加入之前的集合(方案三,五,六)
(和ybt1192放苹果类似)
分这两类讨论:
如果新元素自己成为集合,那么就简化成讨论剩下i-1个元素分到j-1个集合的方案
如果新元素加入原有集合,那么就简化为讨论剩下i-1个元素分到原有j个集合的方案
但是新元素可以加入原有j个集合中任意一个,所以要乘j。
写出方程:fi,j=fi-1,j-1+j*fi-1,j
#include<iostream>
using namespace std;
long long n,k,f[505][505];
int main() {
cin>>n>>k;
for(int i=1;i<=n;i++) {
f[i][i]=1;
f[i][1]=1;
}
for(int i=2;i<=n;i++)
for(int j=2;j<i;j++) {
f[i][j]=f[i-1][j-1]+j*f[i-1][j];
}
cout<<f[n][k]<<endl;
return 0;
}
当然,也可以用滚动数组优化成一维数组:
#include<iostream>
using namespace std;
long long n,k,f[505];
int main() {
cin>>n>>k;
for(int i=1;i<=n;i++) {
f[i]=1;
}
for(int i=2;i<=n;i++)
for(int j=i-1;j>=2;j--) {//因为更新f[j]时要调用f[j-1],为了消除本轮数据造成的影响,需要倒着循环
f[j]=f[j-1]+j*f[j];
}
cout<<f[k]<<endl;
return 0;
}