Java题解 优化青海湖至景点X的租车路线成本 | 豆包MarsCode AI刷题

77 阅读3分钟

优化青海湖至景点X的租车路线成本

问题描述

小F需要从青海湖前往一个遥远的景点X,起点油量为200L,车辆最大油箱容量为400L,消耗规则是每行驶1km消耗1L油。沿途有若干加油站,每个加油站有固定的距离和油价。小F的目标是规划合理的加油方案,最小化从起点到终点的燃油成本。需要满足以下条件:

  1. 到达终点时,油箱剩余不少于200L;
  2. 如果无法到达终点,则返回-1。

解题思路

这是一个经典的动态规划问题。

动态规划建模

状态定义

  • dp[i][j]:到达第 i 个加油站时,油箱中剩余 j 升油的最小花费。

状态转移

  • 从起点直接到达第一个加油站:

    dp[i][k]=dp[0][k+di]dp[i][k]=dp[0][k+d_i]

    其中,did_i 是第一个加油站的距离。

  • 从第 j 个加油站转移到第 i 个加油站:

    dp[i][k]=min(dp[i][k],dp[j][k+dij])dp[i][k]=min⁡(dp[i][k],dp[j][k+d_{ij}])

    其中,dijd_{ij} 是第 j 个加油站到第 i 个加油站的距离。

  • 如果加油,则更新花费:

    dp[i][k]=min(dp[i][k],dp[i][k1]+pi)dp[i][k]=min⁡(dp[i][k],dp[i][k−1]+p_i)

    其中,pip_i 是第 i 个加油站的油价。

边界条件

  • 初始状态:dp[0][200]=0dp[0][200] = 0,表示起点油量为200L时的花费为0;
  • 无法转移时:dp[i][j]=dp[i][j] = ∞

最终目标

  • 找到满足条件的最小 dp[i][j]dp[i][j],且 j>=distance+200j >= distance + 200

代码:

public class Main {
    public static int solution(int distance, int n, List<List<Integer>> gasStations) {
        // 动态规划数组,dp[i][k] 表示到达第 i 个加油站、油量为 k 时的最小花费
        int[][] dp = new int[n + 10][410];
        // 按加油站距离排序
        gasStations.sort((a, b) -> Integer.compare(a.get(0), b.get(0)));
        // 初始化 DP 数组为最大值
        for (int i = 0; i <= n; ++i)
            Arrays.fill(dp[i], Integer.MAX_VALUE);
        // 初始状态:起始点油量 200L,花费为 0
        dp[0][200] = 0;
        // 遍历加油站
        for (int i = 1; i <= n; ++i) {
            for (int j = 0; j < i; ++j) {
                // 油耗计算
                int cost = gasStations.get(i - 1).get(0) - (j == 0 ? 0 : gasStations.get(j - 1).get(0));
                for (int k = cost; k <= 400; ++k) {
                    if (dp[j][k] != Integer.MAX_VALUE) {
                        dp[i][k - cost] = Math.min(dp[i][k - cost], dp[j][k]);
                    }
                }
            }
            // 加油操作
            for (int k = 1; k <= 400; ++k) {
                if (dp[i][k - 1] != Integer.MAX_VALUE) {
                    dp[i][k] = Math.min(dp[i][k], dp[i][k - 1] + gasStations.get(i - 1).get(1));
                }
            }
        }
        // 检查终点状态
        int result = Integer.MAX_VALUE;
        for (int k = distance - (n == 0 ? 0 : gasStations.get(n - 1).get(0)); k <= 400; ++k) {
            if (dp[n][k] != Integer.MAX_VALUE) {
                result = Math.min(result, dp[n][k]);
            }
        }
        return result == Integer.MAX_VALUE ? -1 : result;
    }
    public static void main(String[] args) {
        List<List<Integer>> gasStations1 = Arrays.asList(
                Arrays.asList(100, 1), Arrays.asList(200, 30), Arrays.asList(400, 40), Arrays.asList(300, 20));
        System.out.println(solution(500, 4, gasStations1)); // 输出:4300

        List<List<Integer>> gasStations2 = Arrays.asList(
                Arrays.asList(300, 25), Arrays.asList(600, 35), Arrays.asList(900, 5));
        System.out.println(solution(1000, 3, gasStations2)); // 输出:-1
    }
}

代码解析:

  • dp数组定义

    • dp[i][k]表示到达第i个加油站时,油量为k时的最小成本。
    • 初始化为Integer.MAX_VALUE表示无法到达。
  • 加油站排序

    • 按距离从大到小排序,确保动态规划能够正确计算每一步的状态转移。
  • 初始状态

    • 在起点时,油量为200L,成本为0。
  • 外层循环

    • 遍历每个加油站,计算从之前加油站到当前加油站的所有可能状态。
  • 从上一个状态转移

    • 对于加油站i,如果从加油站ji的油量消耗<= 400L,则可以从j转移到i,同时保持油量不低于0。
  • 加油策略

    • 如果在加油站i当前油量不足,可以选择加油,增加到下一状态。
  • 状态更新

    • 每次更新dp[i][k]为到达状态的最小成本。
  • 遍历所有状态

    • 检查在所有加油站的状态下,是否满足最终到达目标地的条件(剩余油量≥200L)。
  • 结果判断

    • 如果ans仍为Integer.MAX_VALUE,说明无法到达目标地,返回-1;否则返回最小成本。

复杂度分析:

  • 排序复杂度O(nlogn)O(nlog⁡n)

  • 动态规划复杂度:状态转移 O(n2×C)O(n^2×C),其中C=400C=400为油量状态数量。

  • 总复杂度O(n2×400+nlogn)O(n^2×400+nlog⁡n)