首先我们来想一下放苹果的问题:
这是之前利用搜索做过的题目,现在尝试利用递归的思想来解:
(1) 将m个同样的苹果放入到n个一样的盘子中,比如有7个苹果,7个盘子,1,1,5和5,1,1的方法是相同的,因为盘子都一样,苹果也一样,方案数就重合了,所以在搜索解法中的可行性方案是要进行剪枝和不降序枚举的。
(2)递归的思想,本质上就是将大问题转化成小问题的思想,不去考虑冗杂的数据,重在分析和分类,比如这道题,盘子和苹果的数量的不同,势必会影响整体的拆分方案,设f(m,n)为将m个苹果分成n的方案数
当m<n的时候,肯定是有空盘子的,最少有m-n个空盘子,那么f(m,n)=f(m,m);
当m>n的时候,可以有空盘子,也可以没有,如果没有空盘子的话,可以先将n个盘子中每个都放入一个苹果,苹果剩下了m-n个,那么方案数也就是将m-n个苹果放到n个盘子中,
即f(m-n,n);如果有空盘子的话,盘子可以空1个,空2个,空3个…空一个的时候方案数为f(m,n-1)即将m个苹果放到n-1个盘子中,重点来了,那空两个盘子,三个盘子的时候表达式也都要列出来么?不需要,因为递归本质上就是将规模缩小的思想,随着不断递进n-1会逐渐缩小,也就变成了n-2,n-3的规模,即空2个盘子和3个盘子的问题
所以当m>=n的时候,f(m,n)=f(m-n,n)+f(m,n-1)
当m==n的时候,同样的思路,如果没有空盘子f(m,n)=1,如果有空盘子f(m,n)=f(m,n-1)
即f(m,n)=1+f(m,n-1)
再来看规模最小的问题,也就是递归的出口,当m==1的时候,不管有多少盘子,方案数也就是1,当盘子为1的时候,不管有多少苹果,方案数也是1。
#include<bits/stdc++.h>
using namespace std;
long long f(int x,int y)
{
if(x==1||y==1)//当只有一个盘子或者一个苹果的时候
return 1;//只有1种方案
if(x<y)//如果苹果数小于盘子数的话
return f(x,x);
if(x==y)//如果苹果数等于盘子数的话
return 1+f(x,y-1);
if(x>y)//如果苹果数大于盘子数的话
return f(x-y,y)+f(x,y-1);
}
int main()
{
int k,m,n;
cin>>k;
for(int i=1;i<=k;i++)
{
cin>>m>>n;
cout<<f(m,n)<<endl;
}
return 0;
}
解题思路:
(1)现在再来看一下这道题目, 集合的划分,第一个限定条件,集合不能为空,第二个限定条件,任意两个集合都没有相同的元素,第三个限定条件,所有的集合合起来是原来的大集合
(2)那么本质上就是放苹果的问题,现在有m个不同的苹果,放在n个盘子里,并且每个盘子都不为空,问有多少种方法?
(3)根据递归思路来分类为子问题的话,同样m和n的大小会拆解为不同的分法,如果m<n的话,
必然会有空盘子,那么方案数为0,如果m==n的话,每一个盘子放一个苹果,方案数为1,如果m>n的话,因为苹果都是不相同的,先拿出一个苹果来,如果这个苹果单独放一个盘子的话,方案数为f(m,n)=f(m-1,n-1),如果这个苹果和其他苹果在一个盘子里,先将m-1个苹果放到n个盘子中,这一个苹果放到哪里呢?他有n种选择,因为苹果是不一样的!即f(m,n)=f(m-1,n)*n
所以得到f(m,n)=f(m-1,n-1)+f(m-1,n)*n;
#include<bits/stdc++.h>
using namespace std;
long long f(int x,int y)
{
if(x==y||y==1)//如果只有1个盘子或者苹果等于盘子
return 1;
if(x<y)//如果盘子大于苹果数
return 0;
if(x>y)//如果苹果树大于盘子
return f(x-1,y-1)+f(x-1,y)*y;
}
int main()
{
int m,n;
cin>>m>>n;
cout<<f(m,n);
return 0;
}