加油站问题
题目链接
PAT:https://pintia.cn/problem-sets/994805342720868352/problems/994805458722734080
题意
一条线段,起点到终点的距离是D,从起点开始路上有N个加油站,每个加油站的油价都不一样。现在一辆车的油箱容量是Cmax,每单位的油可以跑Davg的路程。现在给定N个加油站各自离起点的距离Di、油价Pi(钱/单位油)。问汽车能否顺利到达终点。如果能,采取怎样的加油策略,花费最少;如果不能,最多能走多少距离。
题解
分析
到达不了终点的情况很好判定:在某两个加油站之间的路程,加满了油也无法通过。此时加满油最多能走的路程+上一个加油站的位置,就是最多能走的距离。
下面考虑在一定能到达情况下,花费最少的策略:
总路程是一定的,每单位的油能跑的路程也是一定的。所以最终使用的油的总量也是定值。直观来看,应该在保证能够继续前行的前提下,在更便宜的加油站加更多的油。
先粗略分析一下,当路过每个加油站的时候,决定是否加油、加多少油的因素有:
首先得保证能在油尽之前到达下一个加油站
其次,如果加油太多,会不会因为前方有更便宜的加油站,导致不划算?
===>毕竟如果有更便宜的加油站,到那里留着更大的空间(带着空油箱去)加更多的油,才是更划算的决策
反之,如果加油太少,会不会因为前方可到达的加油站都比较贵,导致不划算?
===>为了继续前进,必须到前方可到达的加油站之一进行加油,如果他们都比目前的贵,为何不趁现在多加一点油(加满)?
采用的贪心策略
依据上述的分析,先给出贪心策略(后面证明其正确性):
到达某个加油站时,检查后面可到达的加油站的范围(加满油可到达的加油站的范围)
若找到了第一个(离当前加油站最近的)价格低于当前加油站价格的加油站st[next]
- 检查当前的油量
- 若不够到达st[next],则加油到刚好到达那个加油站,前往st[next],开始下一轮选择
- 若够到达,则不加油,直接前往st[next],开始下一轮选择
若每一个可到达的加油站都比当前的贵
- 检查是否能直接到达终点
- 若能直接到达终点
- 检查当前油量是否能到达终点
- 若能,则直接前往终点
- 若不能,则加油至正好能到达终点
- 若不能直接到达终点
- 在当前加油站加满油
- 直接前往后面加油站中油价最低的,开始下一轮选择
最优子结构
对子问题进行定义:当前到达第i个加油站,油箱内的剩余油量是gas,求到达终点的最低代价,记做二元组(i, gas)。此最低代价记作S(i, gas)。
对求解每个子问题时候的"选择"进行定义:选择加add的油,下一次前往第j个加油站,记做二元组(add,j)。对于每个子问题(i, gas),都有一个选择的取值集合\(C\)_\(S(i,gas)\) (Choose_set的简写),满足选择的合理性(不能超过油箱、加油后能到达所选择的加油站):
\[ C\_S(i,gas) =\{(add,j)\Big| i \le j, \ \frac {st[j].dist-st[i].dist}{Davg} \le add+gas\le Cmax \} \]
若原问题的解是最优的,那么当做出选择之后,会得到一个子问题,这个的子问题的解一定是最优的。(若不是最优的,则换成最优的,而选择的代价没有改变,进而得到的新的原问题是更优的,与原本的原问题最优的假设矛盾,反证成立)===>最优子结构得证
所以能够得到以下递推式:
\[ S(i,gas)=\min_{(add,j)\in C\_S(i,gas)}\{add\cdot st[i].price + S(j, gas-\frac{st[j].dist-st[i].dist}{Davg}) \} \]
贪心选择性
这个形式化证明起来比较繁琐,这里简要说明(口胡)
当后续加油站有更便宜的
- 为何要用第一个更便宜的,而不是后面的更便宜的(如果有)
- 粗证:假设不用第一个,那么需要花更多的油到达后面的,行驶这整个部分的油,都是来自与较高价格的油。而用第一个的话,可以在后续段中使用更低价格的油。
- 为何要加油至能正好到达:
- 粗证:假设加更多的油,相当于多加了一部分高价油,而本来这部分油可以被 在到达便宜加油站之后加的油替换掉。因此只需保证能到达这个“第一个更便宜的加油站即可”
- 为何要用第一个更便宜的,而不是后面的更便宜的(如果有)
如果后续加油站没有更便宜的
若能直接到终点
- 为何尽量直接到达终点
- 粗证:既然后面的加油站都比较贵,与其在它们那里停靠加油,不如直接在当前(更便宜的)加油站加满到终点所需的油。
- 为何尽量直接到达终点
若不能直接到终点:
- 为何要加满
- 粗证:为了继续前进,必须在后续加油站的其中一个停靠。最后总得在某个更贵的站点补充油,所以为了能在后续更贵的站点补更少的油,此时应该加得尽可能多。
- 为何要停在后续最低价的加油站
- 粗证:为了继续前进,必须在后续加油站的其中一个停靠。既然停靠的话,应该找更便宜的停靠,进而寻找接下来的机会。 (注:我感觉这段证明真的口胡,但没有严谨证明的思路,恳请大家指点)
- 为何要加满
AC代码
1 |
|