- m个相同的苹果放在n个相同的盘子里,允许有空盘子
- m个相同的苹果放在n个相同的盘子里,不允许有空盘子
- m个相同的苹果放在n个不同的盘子里,允许有空盘子
- m个相同的苹果放在n个不同的盘子里,不允许有空盘子
第一种情况
设f(m,n) 为m个苹果,n个盘子的放法数目,则先对n作讨论,
如果n>m,必定有n-m个盘子永远空着,去掉它们对摆放苹果方法数目不产生影响;即 if(n>m) f(m,n) = f(m,m)
当n<=m时,不同的放法可以分成两类:即有至少一个盘子空着或者所有盘子都有苹果,
前一种情况相当于f(m,n) = f(m,n-1);
后一种情况可以从每个盘子中拿掉一个苹果,不影响不同放法的数目,即f(m,n) = f(m-n,n).
而总的放苹果的放法数目等于两者的和,即f(m,n) =f(m,n-1)+f(m-n,n)。
边界条件为m=0或n=1时,只有一种放法。
#include<iostream>
using namespace std;
int fun(int m,int n){
// 没有苹果 或者 只有一个盘子
if(m==0||n==1)
return 1;
if(m<n)
return fun(m,m);
else
//if((m-n)<=1||n==1)
//return 1;
return fun(m,n-1)+fun(m-n,n);
}
int main(){
int m,n,t;
cin>>t;
while(t--){
cin>>m>>n;
cout<<fun(m,n)<<endl;
}
return 0;
}
第二种情况
盒子不能为空,所以可以当成将m-n个球放到n个盒子里,盒子可以为空。
比如将10个球放到5个盒子里,可以当成5个球放到5个盒子里,盒子能为空。接着,再分情况,将球分为
(5,0,0,0,0)
(4,1,0,0,0)或(2,3,0,0,0)
(3,1,1,0,0)或(2,2,1,0,0)
(2,1,1,1,0)
(1,1,1,1,1)
也就是1个盒子不能为空,2个盒子不能为空,3个盒子不能为空,到5个盒子都不能为空。
用一个函数fun(m,n)来获取m个球放到n个盒子里不能为空的情况(也可以用能为空的,这里用的是不能为空的)
上面的可以转换成
fun(5,1)+fun(5,2)+fun(5,3)+fun(5,4)+fun(5,5)
#include <iostream>
using namespace std;
int fun(int m, int n) {
if (m < n) //m<n返回0
return 0;
if (n == 1 || (m - n) <= 1) //盒子为1时,球只有一个或0(0个即球刚好均分),时都只有一种分配方法
return 1;
int s = 0, count; //s记录总数,count为递归次数
if (m - n < n) //去掉每个盒子分配的一个球后,球的数量少于盒子数,则只要递归球的个数次
count = m - n; //球数少的情况
else
count = n; //盒子数少的情况
for (int i = 1; i <= count; i++) {
s += fun(m - n, i); //递归
}
return s;
}
int main(int argc, char **argv) {
cout << "\t";
for (int i = 1; i <= 15; i++) {
cout << i << "\t";
}
cout << endl;
for (int i = 1; i <= 15; i++) {
cout << i << "\t";
for (int j = 1; j <= 15; j++) {
cout << fun(i, j) << " ";
}
cout << endl;
}
}
第三种情况
思路和盘子不同是差不太多,只是多了一个组合数(在这里用Comd(n, m), 表示从n个中选出m个)。
开始分为两种可能:
当 m < n 时,m个苹果可以在n个盘子里选m个,也就是Comd(n, m)个,身下的(n-m)个盘子已经被考虑过了,而且不会影响接下来的摆放,直接去掉就行,所以方法数有 Comd(n, m) * f(m, m)
当 m >= n时,分为有盘子为空(有0)和盘子均不为空(无0):
有0时:从n个盘子中选出一个不放苹果,有Comd(n, 1)种选法,所以就是Comd(n, 1) * f(m, n-1)
无0时,从m个苹果中先拿出n个放满盘子,这n个苹果对后序摆放没有一点影响,直接去掉就行,然后就变成了(m-n)个苹果摆放在n个盘子问题 也就是 f(m-n, n)
递归出口(同盘子相同时一样)
int f(int m, int n)
{
if (m==0 || n==1)
{
return 1;
}
else if (m < n)
{
return Comd(n, m) * f(m, m);
}
else if (m >= n)
{
return Comd(n, 1)*f(m, n-1) + f(m-n, n);
}
}
第四种情况
#include <iostream>
using namespace std;
int fun(int m, int n) {
if (m < n) //m<n返回0
return 0;
if (n == 1 || m==n)
return 1;
if ((m - n) == 1) //盒子为1时,球只有一个或0(0个即球刚好均分),时都只有一种分配方法
return n;
int s = 0, count; //s记录总数,count为递归次数
if (m - n < n) //去掉每个盒子分配的一个球后,球的数量少于盒子数,则只要递归球的个数次
count = m - n; //球数少的情况
else
count = n; //盒子数少的情况
for (int i = 1; i <= count; i++) {
s += Comd(n, count)*fun(m - n, i); //递归
}
return s;
}