【算法】有趣的小题目——如何让一架飞机飞到终点

题目:假设一架飞机只有1个油箱飞机之间可以相互加油且忽略它们的加油时间。已知一架飞机需要R(R>=1且R为整数)箱油才能从机场A飞到机场B,机场A和B都有无数架飞机,且所有飞机都不能有去无回,问想让一架飞机F从A飞到B至少需要多少架飞机(包括飞机F)?




思考:

当R=1,那么只需要一架飞机就能从A飞到B,Sum(R=1)=1

当R=2,那么飞机F只能飞到A和B的中间,如何才能让他到B呢?

为描述方便,假设A和B的距离为8个单位,那么一个油箱可以飞4个单位

想法1:只要飞机F在4个单位时加满油,那么他就能一直飞到8个单位处

那么如何让飞机F在4单位时满油呢?

想法2a:有另外一架飞机F1在4单位时满油,把所有油给飞机F,但此时是一个死循环,因为如何做到飞机F1在4单位时满油呢?❌

想法2b:有另外两架飞机F1,F2在3单位时满油,在4单位时F1,F2还剩3单位的油,分别给F两单位的油,还剩1单位的油往回飞,最后的状态是F满油到目的地,F1,F2无油在3单位处。此时如何接回F1,F2?

想法3a:只考虑接回F1,在2单位时如果有两架满油飞机F3,F4,飞到3单位时都有3单位的油,分别给F1两单位的油(不给2单位+1单位油/1单位+1单位是处于算法实现的考虑),此时F1回到出发点浪费1单位油,F3,F4无油在2单位处,因为浪费了油,感觉不是最优解❌

回到想法1,这个想法看似没错,但有个设计上的隐形问题。首先根据之前不成功的思路可以看到,这道题其实完全是可以递归实现的(但不一定最优),这就意味着应该有一套相似可重用的逻辑,但是想法1想达成的目的表面上是“让飞机满油”,但实际却默认了“让飞机从没油变成满油”,但在R=2的情况下,在4单位前飞机是不可能的没油的,也就是递归的相似条件并不存在。

所以有

想法2c:有另外一架飞机Ff,在3单位时满油,同时保证F在3单位时满油,在4单位时Ff和F只剩下3单位的油,Ff给一单位油给F,最终F飞到终点,而Ff飞回到2单位处没油

想法3b:此时1.要接回2单位的Ff,因为不需要保证Ff满油了,所以已经到了递归的临界处,需要另外考虑。

2.要保证Ff和F在3单位时满油,先考虑F,递归考虑为有另一架飞机Ff2和F在2单位处满油,操作同理,需要接回在1单位的Ff2。Ff同理,需要接回在1单位的Ff3

先总结一下现在要处理的东西,Ff在2单位处需要接回,Ff2在1单位处需要接回,Ff3在1单位处需要接回

这三者都属于临界状态,分别考虑

如果飞机在2单位处没油,要接回则需要2架飞机(不计没油的那一架)

如果飞机在1单位处没油,要接回则需要1架飞机(不计没油的那一架)

另外需要保证Ff、F、Ff2、Ff3在2单位处满油,属于临界状态(如果继续递归就会浪费油了),一架飞机只需要3架飞机就能在2单位处满油(包括自己的那一架)

加粗加斜加下划线部分不做解释了,和递归算法没有关系,纯粹是逻辑题

综上总述,需要飞机

Sum(R=2)=full(4)
    =2*full(3)+back(2)
    =2*(2*full(2)+back(1))+back(2)
    =16

算法是这么算,但是怎么看起来那么多?

优化一下

Sum(R=2)=full(2)+back(2)
    =3+2
    =5

优化思路有空再更吧。

上一下代码,其实在full计算本体飞机,back不算本体飞机上存在疑惑的,努力说服了自己

int full(int length);
int back(int length);

int count(int R){//R箱油能飞完全程
    int nlength = (R-1) * 4;//假设一箱油能飞4个单位
    return full(nlength);//计算飞了R-1箱油距离还是满油所需要的飞机数
}

int full(int nlength){//飞机飞完nlength/4箱油距离还满油所需要的飞机
    if(nlength == 0) {
        return 0;
    }
    if(nlength == 1) {//飞机飞完1/4箱油距离还满油所需要的飞机
        return 2;
    }
    if(nlength == 2) {//飞机飞完1/2箱油距离还满油所需要的飞机
        return 3;
    }
    if(nlength == 3) {//飞机飞完3/4箱油距离还满油所需要的飞机
        return 2*full(2) + back(3 - 2);
    }
    if(nlength == 4) {//飞机飞完一箱油距离还满油所需要的飞机
        return 2*full(3) + back(4 - 2);
    }
    return 2*full(nlength-1)+back(nlength - 2);
}

int back(int length){//接飞回到length/4处没油飞机所需要的飞机数
    if(length == 0){
        return 0;
    }
    if(length == 1) {//接飞回到1/4箱油处没油飞机所需要的飞机数
        return 1;
    }
    if(length == 2) {//接飞回到1/2箱油处没油飞机所需要的飞机数
        return 2;
    }
    if(length == 3) {//接飞回到3/4箱油处没油飞机所需要的飞机数
        return 2*back(3 - 2) + full(3);
    }
    if(length == 4){//接飞回到1箱油处没油飞机所需要的飞机数
        return 2*back(4 - 2) + full(4);
    }
    return 2*back(length - 2) + full(length);
}

伪通解,不符合规范,意思到了就行。

Sum(R)=full(x)+back(4R-4-x)(x<=4R-4)

MIN(Sum(R))=MIN(Sum(R,x))

你可能感兴趣的:(小玩意儿,算法)