50. 青海湖租车成本 | 豆包MarsCode AI刷题

62 阅读4分钟

问题描述

小F计划从青海湖出发,前往一个遥远的景点X进行旅游。景点X可能是“敦煌”或“月牙泉”,线路的路径是唯一的。由于油价的不断上涨,小F希望尽量减少行程中的燃油成本。车辆的油箱容量为 400L400L,在起始点租车时,车内剩余油量为 200L200L。每行驶 1km1km 消耗 1L1L 油。沿途设有多个加油站,小F可以在这些加油站补充燃油;此外,到达目标景点X还车的时候,需要保证车内剩余的油至少有 200L200L

小F需要你帮助他计算,如果合理规划加油站的加油顺序和数量,最小化从青海湖到景点X的旅行成本(元)。

输入:

  • distance:从青海湖到景点X的总距离(kmkm),距离最远不超过 10000km10000 km
  • n:沿途加油站的数量 (1<=n<=1001 <= n <= 100)
  • gas_stations:每个加油站的信息,包含两个非负整数 [加油站距离起始点的距离(kmkm), 该加油站的油价(元/LL)]

输出:

  • 最小化从青海湖到景点X的旅行成本(元)。如果无法到达景点X,或者到达景点X还车时油料剩余不足 200L200L,则需要返回 -1 告诉小F这是不可能的任务。

问题分析

本题可以使用动态规划的方法。

  1. 初始化

    • 将终点(景点X)作为一个特殊的加油站加入到 gasStations 列表中,距离为 distance,油价为0。
    • 对 gasStations 列表按距离进行排序。
    • 计算每个加油站与前一个加油站的距离,存储在 dis 数组中。
  2. 动态规划表初始化

    • dp[0][200] 初始化为0,表示在起点(青海湖)时,油量为200L,花费为0。
    • 其他 dp[i][j] 初始化为 Infinity,表示初始状态下无法到达这些状态。
  3. 动态规划过程

    • 遍历每个加油站 i,对于每个加油站 i,遍历所有可能的油量剩余 j
    • 对于每个油量剩余 j,再遍历前一个加油站 i-1 的所有可能油量剩余 k
    • 如果从加油站 i-1 出发,到达加油站 i 时油量足够(即 j + dis[i-1] - k >= 0),并且加油站 i-1 有油价信息,则计算从加油站 i-1 到达加油站 i 的花费,并更新 dp[i][j]
  4. 结果检查

    • 检查是否能够到达终点,并且到达终点时油量剩余至少为200L。
    • 如果无法到达终点或油量不足,返回 "Impossible"
    • 否则,返回最小花费。

代码实现(JS版)

function solution(distance, n, gasStations) {
  // Please write your code here
  // distance为总距离,gasStations[n][0]为距离终点的距离,gasStations[n][1]为当前加油站加每公里油的费用
  // 将终点距离加到加油站列表中,方便后续处理(相当于把终点当成最后一个加油站了)
  gasStations.push([distance, 0]);
  // 按照距离加油站(终点)的距离长短排序
  gasStations.sort((a, b) => a[0] - b[0]);

  // 距离数组,表示每个加油站和前一个加油站的距离
  let dis = new Array(n + 1).fill(0);
  dis[0] = gasStations[0][0];
  for (let i = 1; i <= n; i++) {
    dis[i] = gasStations[i][0] - gasStations[i - 1][0];
  }

  // dp[i][j]表示到达第i个加油站油量剩余j时的最小花费
  let dp = new Array(110);
  for (let i = 0; i < 110; i++) {
    dp[i] = new Array(410).fill(Infinity);
  }
  // 起点设置为0
  dp[0][200] = 0;

  // 动态规划过程
  for (let i = 1; i <= n; i++) {
    // 意味着找到最后一个有效的加油站
    if (gasStations[i - 1][0] === distance) {
      n = i - 1;
      break;
    }
    // j表示到达第 i 个加油站时油量剩余为 j 的情况,k表示到达第 i - 1 个加油站时油量剩余为 k 的情况
    for (let j = 0; j < 401; j++) {
      for (let k = 0; k < 401; k++) {
        // 如果从当前加油站的前一个加油站出发,到达当前加油站时油量足够,并且当前加油站有油价信息
        if (j + dis[i - 1] - k >= 0 && gasStations[i - 1].length > 1) {
          // 更新dp[i][j]为到达当前加油站剩余j的最小花费
          let cost = (j + dis[i - 1] - k) * gasStations[i - 1][1];
          dp[i][j] = Math.min(dp[i][j], dp[i - 1][k] + cost);
        }
      }
    }
  }

  if (n < 1 || 200 + dis[n - 1] < 0 || dp[n][200 + dis[n - 1]] === Infinity) {
    return -1;
  }
  return dp[n][200 + dis[n - 1]];
}

function main() {
    // You can add more test cases here
    const gasStations1 = [
        [100, 1],
        [200, 30],
        [400, 40],
        [300, 20],
    ];
    const gasStations2 = [
        [100, 999],
        [150, 888],
        [200, 777],
        [300, 999],
        [400, 1009],
        [450, 1019],
        [500, 1399],
    ];
    const gasStations3 = [[101], [100, 100], [102, 1]];
    const gasStations4 = [
        [34, 1],
        [105, 9],
        [9, 10],
        [134, 66],
        [215, 90],
        [999, 1999],
        [49, 0],
        [10, 1999],
        [200, 2],
        [300, 500],
        [12, 34],
        [1, 23],
        [46, 20],
        [80, 12],
        [1, 1999],
        [90, 33],
        [101, 23],
        [34, 88],
        [103, 0],
        [1, 1],
    ];

    console.log(solution(500, 4, gasStations1) === 4300);
    console.log(solution(500, 7, gasStations2) === 410700);
    console.log(solution(500, 3, gasStations3) === -1);
    console.log(solution(100, 20, gasStations4) === 0);
    console.log(solution(100, 0, []) === -1);
}

main();