优化青海湖至景点X的租车路线成本
问题描述
小F需要从青海湖前往一个遥远的景点X,起点油量为200L,车辆最大油箱容量为400L,消耗规则是每行驶1km消耗1L油。沿途有若干加油站,每个加油站有固定的距离和油价。小F的目标是规划合理的加油方案,最小化从起点到终点的燃油成本。需要满足以下条件:
- 到达终点时,油箱剩余不少于200L;
- 如果无法到达终点,则返回-1。
解题思路
这是一个经典的动态规划问题。
动态规划建模
状态定义:
dp[i][j]:到达第i个加油站时,油箱中剩余j升油的最小花费。
状态转移:
-
从起点直接到达第一个加油站:
其中, 是第一个加油站的距离。
-
从第
j个加油站转移到第i个加油站:其中, 是第
j个加油站到第i个加油站的距离。 -
如果加油,则更新花费:
其中, 是第
i个加油站的油价。
边界条件:
- 初始状态:,表示起点油量为200L时的花费为0;
- 无法转移时:。
最终目标:
- 找到满足条件的最小 ,且 。
代码:
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,如果从加油站j到i的油量消耗<= 400L,则可以从j转移到i,同时保持油量不低于0。
- 对于加油站
-
加油策略:
- 如果在加油站
i当前油量不足,可以选择加油,增加到下一状态。
- 如果在加油站
-
状态更新:
- 每次更新
dp[i][k]为到达状态的最小成本。
- 每次更新
-
遍历所有状态:
- 检查在所有加油站的状态下,是否满足最终到达目标地的条件(剩余油量≥200L)。
-
结果判断:
- 如果
ans仍为Integer.MAX_VALUE,说明无法到达目标地,返回-1;否则返回最小成本。
- 如果
复杂度分析:
-
排序复杂度:
-
动态规划复杂度:状态转移 ,其中为油量状态数量。
-
总复杂度:。