酷壳上面有这样一道题:
许多人回复了答案和简要解答过程,但粗粗浏览一遍,即使是正确的答案,我感觉在很大程度上也是通过直觉得到了一个前提条件,在此基础上计算得出结果,缺乏比较严谨的证明和思考过程,因此也容易让人有天马行空的感觉。那么,对于这个问题,我们应该如何思考?
首先,从题目的描述,可以得到两个推断:
1、火车需要来回把煤运到若干个中间点后再出发的(如果一次过去不回头,到了终点煤量就是0了),运送过程中需要消耗燃料;
2、在中间点M时,其他地方不存在剩余的煤,否则不是最优解。
对于推断2,证明如下:假设其他地方有剩余的煤,并假设放着煤的地方在N点。若N点在M点的后面,那火车要么回头把那部分煤运到中间点,要么直接把那部分煤丢弃不管,这两种情况都会最终得到“N点没有煤”的结果;若N点在中间点前方m公里的位置,那么为了把煤从M点运到N点,需要消耗至少2m吨煤(因为火车是从中间点出发),那么,先把所有煤运到M点,再把部分煤运送到N点,这种情况下火车在M点可以满载出发,能运到N点的煤量必然比较多。因此这样得到的结果一定优于前者。所以其他地方无剩余的煤。
因此,从上面的推断,会得到一个更普适的问题:n吨煤经过k公里后,最多还剩多少吨?由于有若干中间点,那我们设下一个中间点离本次起点的距离为x。然后问题就转化成:对于给定的n和k,如何求x,使得本问题有最优解。
显然,这是一个递推的思路,看到“递推”和“最优解”,很自然就会想到动态规划。这是本问题的第二种解法,咱下集再议。
要运到x公里以外的下一个中间点,火车需要若干来回才能把煤运过去。那么,火车需要多少次来回呢?我们先算一算,看看结果有啥规律:
首先,对于x是否有限制?当然有,要跑到前方x公里处, 那火车剩下的煤量得足够返回才行。所以若火车需要返回,因此x<500。
1、若n<=1000,显然火车不需要来回,直接带上所有的煤往前跑,跑到前方x公里时,能放下的煤量为min(1000-x, k-x)。若起点煤量n>1000,则有部分煤被浪费。
2、若n比较大,火车可以通过一次返回得到更多的煤量积累,则跑到前方x公里处消耗的煤量为3x(一个来回再加上单程),由于火车从起点出发两次,累计最多拿2000吨煤,一共运2000吨煤,所以最后该点能放下的煤量为min(2000-3x, n-3x)。若起点煤量n>2000,则有部分煤被浪费。
3、若n再大一点,火车可以两次来回跑到前方x公里处,那两次来回消耗的总煤量为5x,因为起点最多就3000吨煤,所以n<=3000,最后该点能放下的煤量为min(3000-5x, n-5x) = n-5x。
我们注意到,若决定了火车返回的次数,则途中消耗煤量与x为线性关系。也就是说,火车直接选前方x公里的N点作为下一个中间点,与火车在中途再选若干个中间点,两种走法消耗的煤量是一样的。因此,“选取下一个中间点”实际上不重要,我们只需要决定,对于哪段路程,使用火车返回几次的走法就可以了。由于对于火车返回两次、一次、零次的走法,累计前进x公里后剩余煤量为n-5x、min(2000-3x, n-3x)、min(1000-x, k-x),在n>2000的时候,我们可以使用让火车返回两次的走法,把n-2000吨煤烧掉;n>1000的时候,我们可以使用让火车返回一次的走法把n-1000吨煤烧掉;n<=1000的时候,带上所有家当往前奔吧!这个就是最优解。
画个函数图像可以更直观地看到答案。用横轴表示路程,纵轴表示煤量,图上的一点(k, n)表示在离终点k公里处剩下煤量。火车返回两次时位置和煤量的关系使用黑线表示(斜率-5)、返回一次时位置和煤量的关系使用红线表示(斜率-3),返回零次时位置和煤量的关系使用蓝线表示(斜率-1),则黑线的起点只能在n=3000水平线以下,红线的起点在n=2000水平线以下,蓝线的起点在n=1000水平线以下。我们要做的事情,就是把这几条线连起来,得到一条贯穿左右的通路,右边终点的高度就表示最终剩余的煤量。
看着这个图,最优解一目了然。
下集预告:本问题动态规划的解法。