火车运煤(骆驼运胡萝卜)问题的最优解

题目来源,http://coolshell.cn/articles/4429.html,

原始题目是这样的,你是山西的一个煤老板,你在矿区开采了有3000吨煤需要运送到市场上去卖,从你的矿区到市场有1000公里,你手里有一列烧煤的火车,这个火车最多只能装1000吨煤,且其能耗比较大——每一公里需要耗一吨煤。请问,作为一个懂编程的煤老板的你,你会怎么运送才能运最多的煤到集市?

我改成一般性的形式,题目如下: 火车送a0吨煤到s公里外的市场,火车的能耗是每公里k吨,火车的容量是C吨,请问可以将多少吨煤运到市场? 

将a0吨煤向前运送dx公里,除了最后一次消耗k*dx,其它的都需要一次折返共消耗2*k*dx,所以最能量消耗就是 (2*(ceil(a0/C)-1)+1)*k*dx

余量就是a1=a0-(2*(ceil(a0/C)-1)+1)*k*dx,设定一个运送增量dx, 反复迭代,直到到达目的地S,最后的a1就是最后结果。

迭代的次数根据总路程S和颗粒度大小dx而定。将dx取为一个极小值如0.01就可接近极限。javascript 代码如下,

      function geta1(a0, s, dx, c, k) {      //a0,初始值,s,距离,c,火车容量,k,能耗系数,dx步进量,设置为较小的值如0.01可得最优的结果
//返回值,到达目的地的剩余量
          var a1;
          for (var i = 0; i < s / dx; i++) {
              a1 = a0 - k * dx * (2 * (Math.ceil(a0 / c) - 1) + 1);
              a0 = a1;
          }
          return a1;
      }

用特例中的数字代入原题目,geta1(3000, 1000, 0.1, 1000, 1)  就得最后结果533.2

当然,上述思路是用程序的观点来算的,如果用数学来算的话,象是有关极限积分的,因为里面有一个取整函数,我不懂数学连续函数里怎么处理这个,所以没法给出解析解。

我当初没有看原始出处的答案和评论,自己算出了这个结果,还有点小得意。后来看评论,受9楼Thkfly的启发,改进了一下,不必步步推进,可以将所有的煤向前运一段距离,保证下一个运煤点为火车容量的整数倍就可以了。于是就有了下边的改进方案。这才使得这个问题有了可行性。

       function geta1X2(a0, s, c, k) {
           var x, dx;
           x = 0;
           while (a0 > c && x < s) {
               dx = (a0 - c * (Math.ceil(a0 / c) - 1)) / (k * (2 * (Math.ceil(a0 / c) - 1) + 1));            //将现在的煤向上取整减一,
               if((x+dx)>s){
                   dx = s-x;
               }               
               a0 = a0 - k * dx * ((2 * (Math.ceil(a0 / c) - 1) + 1));
               x += dx;
               alert("这一次将所有煤运送到离出发点" + x + ";剩余煤" + a0); //中间结果
               
           }
           return a0;         //最终结果
       }

geta1X2(5500,700,1000,0.5);


结果如下,

这一次将所有煤运送到离出发点90.9090909090909;剩余煤5000

这一次将所有煤运送到离出发点313.13131313131316;剩余煤4000

这一次将所有煤运送到离出发点598.8455988455989;剩余煤3000

这一次将所有煤运送到离出发点700;剩余煤2747.1139971139974


你可能感兴趣的:(最优解,算法)