问题描述
小F计划从青海湖出发,前往一个遥远的景点X进行旅游。景点X可能是“敦煌”或“月牙泉”,线路的路径是唯一的。由于油价的不断上涨,小F希望尽量减少行程中的燃油成本。车辆的油箱容量为 ,在起始点租车时,车内剩余油量为 。每行驶 消耗 油。沿途设有多个加油站,小F可以在这些加油站补充燃油;此外,到达目标景点X还车的时候,需要保证车内剩余的油至少有 。
小F需要你帮助他计算,如果合理规划加油站的加油顺序和数量,最小化从青海湖到景点X的旅行成本(元)。
输入:
distance:从青海湖到景点X的总距离(),距离最远不超过 。n:沿途加油站的数量 ()gas_stations:每个加油站的信息,包含两个非负整数 [加油站距离起始点的距离(), 该加油站的油价(元/)]
输出:
- 最小化从青海湖到景点X的旅行成本(元)。如果无法到达景点X,或者到达景点X还车时油料剩余不足 ,则需要返回
-1告诉小F这是不可能的任务。
问题分析
本题可以使用动态规划的方法。
-
初始化:
- 将终点(景点X)作为一个特殊的加油站加入到
gasStations列表中,距离为distance,油价为0。 - 对
gasStations列表按距离进行排序。 - 计算每个加油站与前一个加油站的距离,存储在
dis数组中。
- 将终点(景点X)作为一个特殊的加油站加入到
-
动态规划表初始化:
dp[0][200]初始化为0,表示在起点(青海湖)时,油量为200L,花费为0。- 其他
dp[i][j]初始化为Infinity,表示初始状态下无法到达这些状态。
-
动态规划过程:
- 遍历每个加油站
i,对于每个加油站i,遍历所有可能的油量剩余j。 - 对于每个油量剩余
j,再遍历前一个加油站i-1的所有可能油量剩余k。 - 如果从加油站
i-1出发,到达加油站i时油量足够(即j + dis[i-1] - k >= 0),并且加油站i-1有油价信息,则计算从加油站i-1到达加油站i的花费,并更新dp[i][j]。
- 遍历每个加油站
-
结果检查:
- 检查是否能够到达终点,并且到达终点时油量剩余至少为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();