题目说明
对工程师而言,确保电源是最重要的事情。
不仅是 PC,当智能手机、平板电脑、数码相机等电量不足时,我们也肯定要四处寻找插座。
不过,多人共用的时候就必须共享插座,这时插线板就会派上用场。
一般的插线板除了有延长线,还会有多个插口。
这里假设有双插口和三插口的插线板。
墙壁上只有 1 个插座能用,而需要用电的电器有 n 台,试考虑此时应如何分配插线板。
举个例子,当 n = 4 时,如 图 21 所示,有 4 种插线板插线方法
(使用同一个插线板时,不考虑插口位置,只考虑插线板的连接方法。
另外,要使插线板上最后没有多余的插口)。
求 n = 20 时,插线板的插线方法有多少种(不考虑电源的功率问题)?
思路
1.第一个插排有2孔或者3孔
2.如果第一个插排是2孔的,则两个分配方案进行组合
3.如果第一个插排是3孔的,则三个分配方案进行组合
4.定义一个递归方法f(n),返回值是留出n个孔的插排分配方式有多少种
5.比较特殊的是2孔或3孔中孔数相同的情况
(比如第一个插排是2孔插排,
左孔的分配方案是留出3个孔:f(3),
右孔的分配方案也是留出3个孔:f(3),
此时需要排除重复的组合方式)。
规律如下表所示,最终的公式已直接应用在代码中
单孔分配方案 f(i) | 2孔组合 | 3孔组合 |
---|---|---|
1 | 1 | 1 |
2 | 3 | 4 |
3 | 6 | 10 |
4 | 10 | 20 |
5 | 15 | 35 |
n | 1+2+3+4+5+..+n | (1+3+6+10+15+...n(n+1)/2) |
n | n(n+1)/2 | n(n+1)(n+2)/6 |
代码
public static void main(String[] args) {
System.out.println(f(20)); // 63877262
}
/**
* 留出n个孔的插排分配方式有多少种
*/
private static int f(int n){
// 入参检查
if(n <= 0) throw new IllegalArgumentException("入参不合法!n <= 0");
if(n == 1) return 1; // 留出1个孔,那就是空着,不放插排
int count = 0;
// 第一个插排是2孔插排
for(int i = 1; i <= n / 2 ; i ++){
int j = n - i;
if(i == j){
count += f(i) * (f(j)+1) / 2; // 公式 n(n+1)/2
}else{
count += f(i) * f(j);
}
}
// 第一个插排是3孔插排
for(int i = 1; i <= n/3; i ++){ // 孔数i最少[1 , n/3 ]
for(int j = i; j <= (n-i)/2; j ++){ // 孔数j居中[i , (n-i)/2]
int k = n - i - j; // 孔数k最大 n - i - j
if(k <= 0) continue;
if(i == j && j == k){
count += ( f(i) * (f(j) + 1) * (f(k) + 2) ) / 6; // 公式 n(n+1)(n+2)/6
}else if(i == j){
count += f(i) * (f(j) + 1) * f(k) / 2;
}else if(j == k){
count += f(i) * f(j) * (f(k) + 1) / 2;
}else if(i == k){
count += f(i) * f(j) * (f(k) + 1) / 2;
}else{
count += f(i) * f(j) * f(k);
}
}
}
return count;
}
结果
63877262