放鸡蛋问题

我们先来看看题目:

Erin买了不少鸡蛋,她发现一天吃不完这么多,于是决定把n个同样的鸡蛋放在m个同样的篮子里,允许有的篮子空着不放,请问共有多少种不同的放法呢?
注意:2,1,1和1,2,1 是同一种分法。

Input 第一行是测试数据的数目t(0 <= t <= 20)。以下每行均包含二个整数m和n,以空格分开。1<=m,n<=10。

Output 对输入的每组数据m和n,用一行输出相应的结果。

例如:

Input:

4

3 8

4 7

2 4

4 2

Output:

10

11

3

2

(注意结尾有换行)

第一次接触这种题目,大家最头疼的应该是例如(1, 2, 1)和(1, 1, 2)一样的重复问题,如何解决重复问题,是该题的一大难点。

我说说我的思路。

根据题意我们可以大概分成三种情况:鸡蛋数目多于篮子数目,鸡蛋数目等于篮子数目,鸡蛋数目少于篮子数目。

首先,我们来看看最简单的一种情况:鸡蛋数目少于篮子数目。

很明显,鸡蛋少的时候就算每个篮子都放一个鸡蛋,必定会有篮子放不满,因此我们很容易就可以将这种情况简化成为:将n个鸡蛋放入m个篮子(n <m)。

然后,我们再来看看另外一种情况:鸡蛋数目多于篮子数目

鸡蛋数目多于篮子数目时,很显然每个篮子都可以放满,当然也可以只有一个篮子放满,但是再放鸡蛋的过程中,很容易发生重复的情况,这时候我们不妨这样思考:

假设共有M个篮子,N个鸡蛋。现在我们先假设M个篮子都放满且每个篮子都只放一个鸡蛋,放入鸡蛋后剩下的鸡蛋数目为N-M,再让剩下的N-M去放M个篮子,此时我们不继续往下讨论。再假设M-1个篮子都只放满一个鸡蛋,与前面同样的道理,我们可以得到让N-(M-1)个鸡蛋放M-1个篮子……这样一个个增加空篮子的数目直到非篮子数目为2或1。

为了更清楚地说明这种原理,我们设一个放鸡蛋函数为f(M, N) (M为篮子数目,N为鸡蛋数目),然后我们引用例子中M= 4, N= 7,我们就可以进行拆分:

f(4,7)= f(4, 3)+f(3, 4)+f(2, 5)+f(1, 6)
= [f(3, 3)]+[f(3, 1)+f(2, 2)+f(1, 3)]+[f(1, 4)+f(2, 3)]+[f(1, 6)]
=[f(3, 3)]+[f(1, 1)+f(2, 2)+f(1, 3)]+[f(1, 4)+f(2, 3)]+[f(1, 6)]

我们发现当篮子数目M= 2或M= 1时, 分类数目是显而易见的当M= 1时, f(1,N)= 1; 当M= 2时,f(2, N)= N/2(当N为奇数取整)(注意当篮子数目为2时要求两个篮子都放,不存在一个篮子不放的情况)。于是我们得到f(3, 1)= 1, f(2, 2)= 1, f(1, 3)= 1, f(1, 4)= 1, f(2, 3)= 1,f(1, 6)= 1。

我们现在还不知道f(3, 3)的值。

所以我们必须讨论最后一种情况:鸡蛋数目等于篮子数目。

如果按照之前的拆分方式,我们可以将f(3, 3)拆分为:

f(3, 3)= f(3, 0)+ f(2, 1)+ f(1, 2)= f(3, 0)+ f(1, 1)+ f(1, 2)

根据上述的方法我们很快就得到 f=f(2, 1)=f(1, 1)= 1, f(1, 2)= 1。但是我们不知道怎么求f(3, 0)。

为了搞清楚发f(3, 0)是什么,我们认真思考将3个鸡蛋放3个篮子的情况:3个篮子放都各放1个鸡蛋;1个篮子放1个鸡蛋,一个放2个,另一个不放;3个鸡蛋都放进1个鸡蛋,另外两个不放。我们发现1个篮子放1个鸡蛋,一个放2个,另一个不放的情况对应于f(2, 1)即f(1, 1),3个鸡蛋都放进1个鸡蛋,另外两个不放的情况对应于f(1, 2),剩下的3个篮子放都各放1个鸡蛋的情况只能对应于f(3, 0)。这样我们就知道f(3, 0)= 1。所以

f(3, 3)= f(3, 0)+ f(2, 1)+ f(1, 2)= f(3, 0)+ f(1, 1)+ f(1, 2)=1+1+1=3

类似我们试着去求f(4, 4),我们同样发现f(4, 0)= 1。所以根据不完全归纳,我们可以推出f(M, 0)= 1。

现在再回到之前的例子。

f(4,7)= f(4, 3)+f(3, 4)+f(2, 5)+f(1, 6)
= [f(3, 3)]+[f(3, 1)+f(2,2)+f(1, 3)]+[f(1, 4)+f(2, 3)]+[f(1, 6)]
=[f(3, 3)]+[f(1, 1)+f(2, 2)+f(1, 3)]+[f(1, 4)+f(2, 3)]+[f(1, 6)]
= 3+1+2+1+1+1+1+1=11

很明显这就是例子给出的结果。

如果我们将这种方法转化为代码时,我们要实现这种拆分,我们必须用到递归,注意写代码时将基本情况交代清楚,否则程序将出现错误。

我写的代码如下:

#include<stdio.h>
int main() { 
    int put(int basket, int egg);//引入f(M, N)函数 
    int number, i, egg[100], basket[100]; 
    scanf("%d", &number); 
    for (i= 0; i< number; i++) { 
        scanf("%d", &basket[i]); 
        scanf("%d", &egg[i]); 
    } 
    for (i= 0; i< number; i++) { 
        printf("%d\n", put(basket[i], egg[i])); 
    } 
}

int way= 0;

int put(int basket, int egg) { 
    int i; 
    int sum= 0; 
    if (basket<= egg) {
    //鸡蛋数目多于篮子数目的情况 
       if (basket== 2) way= egg/2;
       //f(2, N)情况 
       if (basket== 1) way= 1;
       //f(1, N)情况 
       if (egg== 0) way= 1;
       //f(M, 0)情况 
       else {
          if (egg>= 0) { 
             for (i= basket; i>= 1; i--) { 
                sum+= put(i, egg- i); 
             } 
             way= 0; 
             way+= sum; 
          }//进行拆分并对递归结果进行累加 
       } 
    } 
    else if (basket> egg) put(egg, egg);
     //鸡蛋数目少于篮子数目的情况 
    else {
    //篮子数目等于鸡蛋数目的情况 
       sum= 0; 
       for (i= basket; i>= 1; i--) { 
            sum+= put(i, egg-i); 
       } 
       way= sum; 
    } 
    return way;//输出最终结果 
}

另外附上标答的代码,用的就是我的方法,但标答的代码更简洁

#include<stdio.h>
int egg(int m, int n);

int main() { 
   int t; 
   // m for baskets, n for eggs 
   int m, n; 
   int result = 0; 
   scanf("%d", &t); 
   while (t--) {
      scanf("%d %d", &m, &n); 
      result = egg(m , n); 
      printf("%d\n", result); 
   } 
   return 0; 
}

int egg(int m, int n) { 
   if (m == 1 || n == 0) return 1; 
   if (n < 0) return 0; 
   return egg(m-1, n) + egg(m, n-m); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值