50.优化青海湖至景点X的租车路线|Marscode AI刷题

129 阅读7分钟

1.题目

问题描述

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

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

输入:

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

输出:

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

测试样例

样例1:

输入:distance = 500, n = 4, gas_stations = [[100, 1], [200, 30], [400, 40], [300, 20]]

输出:4300

样例2:

输入:distance = 1000, n = 3, gas_stations = [[300, 25], [600, 35], [900, 5]]

输出:-1

样例3:

输入:distance = 200, n = 2, gas_stations = [[100, 50], [150, 45]]

输出:9000

样例4:

输入:distance = 700, n = 5, gas_stations = [[100, 10], [200, 20], [300, 30], [400, 40], [600, 15]]

输出:9500

样例5:

输入:distance = 50, n = 1, gas_stations = [[25, 100]]

输出:5000

2.思路

初步分析

起始点租车时,车内剩余油量为 200L。每行驶 1km 消耗 1L 油。到达目标景点X还车的时候,需要保证车内剩余的油至少有 200L。所以沿途中需要加油的量和distance的值相等。

最好的情况是在最便宜的加油站买够需要的油,如果做不到,可以在其他车站买油,足够撑到便宜的油站即可。

样例1:

输入:distance = 500, n = 4, gas_stations = [[100, 1], [200, 30], [400, 40], [300, 20]]

输出:4300

先将gas_stations按距离排序:[[100, 1], [200, 30], [300, 20], [400, 40]]。

最好的情况是在最便宜的加油站买够500L:

在[100, 1]加油站时,油箱空余300L,此时花费1*300=300元;

在[200, 30]加油站时,油箱内的油足够开到下一个加油站,此处不加油;

在 [300, 20]加油站,油箱空余200L,这个加油站是第二便宜的加油站,所以在此加满油,花费200*20=4000元,此时已经加够500L油,最小花费300+4000=4300元。

样例2:

输入:distance = 1000, n = 3, gas_stations = [[300, 25], [600, 35], [900, 5]]

输出:-1

开不到第一个加油站就没油了,返回-1。

样例3:

输入:distance = 200, n = 2, gas_stations = [[100, 50], [150, 45]]

输出:9000

优先在[150, 45]加油,油箱里的油足够开到这个油站,花费45*200=9000元

样例4:

输入:distance = 700, n = 5, gas_stations = [[100, 10], [200, 20], [300, 30], [400, 40], [600, 15]]

输出:9500

在[100, 10]加300L油,花费300*10=3000元

在[200, 20]加100L油,花费100*20=2000元

在[600, 15]加300L油,花费300*15=4500元

总计9500元

样例5:

输入:distance = 50, n = 1, gas_stations = [[25, 100]]

输出:5000

50*100=5000元

动态规划

定义 dp[i][j] 表示在第 i 个加油站加油后,油箱内有 j L油时的最小花费。初始状态是 dp[0][200] = 0,即在起始点有200L油且花费为0。然后逐步计算每个加油站的最优加油策略。

3.代码

不用动态规划有点写不下去

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int solution(int distance, int n,
                     std::vector<std::vector<int>> gas_stations) {
    // Please write your code here
    // 加油站按距离排序
    sort(gas_stations.begin(), gas_stations.end(), [](const vector<int>& a, const vector<int>& b){
        return a[0] < b[0];
    });
    // for (const auto& row : gas_stations) {
    //     for (int val : row) {
    //         cout << val << " ";
    //     }
    //     cout << "\n";
    // }
    // 到第一个加油站的距离大于200,如果无法到达景点X
    if (gas_stations[0][0] > 200) {
        return -1;
    }
    // 如果加油站间距大于400, 无法到达景点X
    for (int i = 0; i < n - 1; i++) {
        if ((gas_stations[i + 1][0] - gas_stations[i][0]) > 400) {
            return -1;
        }
    }
    // 加油站按价格排序
    std::vector<std::vector<int>> gas_stations_value = gas_stations; 
    sort(gas_stations_value.begin(), gas_stations_value.end(), [](const vector<int>& a, const vector<int>& b){
        return a[1] < b[1];
    });
    int cur_gas = 200; //当前油箱中的油量
    int cost = 0; //花费
    vector<int> cheapest = gas_stations_value[0]; // 指向当前最便宜的油站
    // 按照距离从近到远遍历加油站
    for (auto it = gas_stations.begin(); it != gas_stations.end(); ++it) {
        // 更新油量
        if (it == gas_stations.begin()) {
            cur_gas = gas_stations[0][0];
        } else {
            cur_gas = 
        }
        // 如果走到了最便宜的油站,加满
        if (it == std::find(gas_stations.begin(), gas_stations.end(), cheapest)) {  // 正确
            
        }
    }
    return -1;
}

int main() {
  std::vector<std::vector<int>> gasStations1 = {
      {100, 1}, {200, 30}, {400, 40}, {300, 20}};
  std::vector<std::vector<int>> gasStations2 = {
      {100, 999},  {150, 888},  {200, 777}, {300, 999},
      {400, 1009}, {450, 1019}, {500, 1399}};
  std::vector<std::vector<int>> gasStations3 = {{101}, {100, 100}, {102, 1}};
  std::vector<std::vector<int>> 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}};

  std::cout << (solution(500, 4, gasStations1) == 4300) << std::endl;
  std::cout << (solution(500, 7, gasStations2) == 410700) << std::endl;
  std::cout << (solution(500, 3, gasStations3) == -1) << std::endl;
  std::cout << (solution(100, 20, gasStations4) == 0) << std::endl;
  std::cout << (solution(100, 0, std::vector<std::vector<int>>{}) ==
                -1)
            << std::endl;

  return 0;
}

动态规划:

import sys
def solution(distance, n, gas_stations):
    # Please write your code here
    maxCapacity = 400
    inf = sys.maxsize

    # 按照加油站位置升序排序
    gas_stations.sort(key=lambda x: x[0])

    # 计算每个加油站之间的距离
    dis = [gas_stations[0][0]] # 初始化时只有第一个加油站的距离
    for i in range(1, n):
        dis.append(gas_stations[i][0] - gas_stations[i - 1][0])
    # 初始化 dp 数组,dp[i][j] 表示在第 i 个加油站加油后,油箱内有 j L油时的最小花费
    dp = [[inf] * (maxCapacity + 1) for _ in range(n + 2)]
    dp[0][200] = 0
    # 动态规划计算最小花费
    for i in range(1, n + 1):
        for j in range(maxCapacity + 1): # 当前站剩余油量
            for k in range(maxCapacity + 1): # 前一站剩余油量
                # 条件:加油后的油量合法,并能支撑到当前站
                if j + dis[i - 1] - k >= 0 and k >= dis[i - 1]:
                    dp[i][j] = min(dp[i][j], dp[i-1][k] + (j + dis[i-1] - k) * gas_stations[i - 1][1])
    
    # 计算到达终点时的剩余油量需求
    remaining_fuel = 200 + distance - gas_stations[n - 1][0]

    if remaining_fuel > maxCapacity or remaining_fuel < 0:
        return -1
    
    # 从剩余油量需求到最大油量,找出最小花费
    result = inf
    for j in range(remaining_fuel, maxCapacity + 1):
        result = min(result, dp[n][j])
    
    # 如果结果仍然是 inf,说明无法到达终点
    if result == inf:
        return -1
        
    return result

j + dis[i - 1] - k >= 0

  • 假设从上一个站点到当前站点需要加油 x,则有: k−dis[i−1]+x=j
  • 整理得: x=j+dis[i−1]−k
  • 因为加油量 x 必须大于等于 0,得出条件: j+dis[i−1]−k≥0

4.参考资料

青训营刷题日记6| 豆包MarsCode AI刷题优化青海湖至景点X的租车路线成本 在现代生活中,随着油价的不断上涨,出 - 掘金