如题所示,这是我2023年9月份,参与华为OD面试的时候,进入第三轮面试,面试官为了考算法,给了这道题,当时我没做出来,最近研究了一下这个题目。
这个题目其实就是普通的排列组合,做了一个变种:把n-mk个球放入m个盒子,盒子容许为空,求总放法。
一般而言,如果n个球,放入m个盒子,如果盒子不为空那么结果就是:
这个结果的思路就是有n个球,那么排成一排,它们之间有n-1个空隙,放入m个盒子,就是分成m份,那么就需要在n-1个空隙中插入m-1个隔板。所以,这个方法也叫隔板法或者挡板法。
比如,我们要把3个球,分成2份,不容许空。最直观的做法,就是:

以上的结果用公式表示:
改变一下条件,如果容许分到的结果为空,那么结果就是:
思路和上面类似,只不过增加了为空的情况:

套用公式:
结合本题,该题多了一个条件,就是每个盒子放k个球,那么剩下n-mk个球,放入m个盒子,每个盒子可以为空,结合以上的结论公式就是:
有了公式,算法就简单了,排列组合结果计算,需要使用到阶乘。
如下是通过java实现的算法示例:
java
/*
* xxx co.ltd Copyright @ 2023-2023 All Rights Reserved
*/
package com.xxx.hw;
import java.util.Scanner;
/**
* 描述信息
*
* @author Administrator
* @since 2023/9/27 14:58
* 题目描述
* 一个排列组合问题:有m个盒子(盒子与盒子之间是不同的),n个球(球与球之间是完全相同的),要求每个盒子里至少放k个球,共有多少种不同的方法?
* <p>
* 解答要求
* 时间限制:1000ms, 内存限制:100MB
* 输入
* 每组测试用例占一行,包含三个整数m,n,k(1<=m<=100,1<=n<=1000,0<=k<=20)
* <p>
* 输出
* 共有多少种不同的方法,由于结果可能超出整数int范围,输出对5201314取余后的值。
* <p>
* 样例
* 输入样例 1
* <p>
* 2 3 1
* 输出样例 1
* <p>
* 2
*/
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
int boxNum = scanner.nextInt();
int ballNum = scanner.nextInt();
int kNum = scanner.nextInt();
System.out.println(permutation(boxNum, ballNum, kNum));
}
scanner.close();
}
public static int permutation(int boxNum, int ballNum, int k) {
int remain = ballNum - k * boxNum;
if (remain == 0) {
return 1;
}
return getResult(boxNum, remain);
}
public static int fun(int n) {
if (n < 2)
return n;
return n * fun(n - 1);
}
public static int getResult(int m, int n) {
return fun(n+m-1)/(fun(m-1)*fun(n));
}
}
运行结果验证:

这个结果第一个示例是把3个球放入2个盒子,每个盒子至少1个球,最简单的就是:1+2=3,2+1=3两种放法。
第二个示例是把10个球,放入3个盒子,每个盒子至少2个球,综合分析10-3*2=4,剩下4个球,放入3个盒子,容许空盒,按照公式:
另外,附上一个推导公式:

这里面,盒子不为空的情况,我们很容易理解,就是,但是容许为空,这个公式是通过如下公式推导过来的: