动态规划问题分享(二)| 豆包MarsCode AI刷题

38 阅读4分钟

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

问题描述

小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

代码解答

def solution(distance, n, gas_stations):
    gas_stations = [[0, 0]] + gas_stations + [[distance, 0]]
    n += 2

    gas_stations.sort(key=lambda x: x[0])

    dp = [[float('inf')] * 401 for _ in range(n)]
    dp[0][200] = 0

    for i in range(1, n):
        dist = gas_stations[i][0] - gas_stations[i-1][0]
        price = gas_stations[i][1]
        stop = True

        if i == n-1:
            if 200 + dist <= 400:
                return dp[i-1][200+dist]
            else:
                return -1

        for j in range(401):
            if dp[i-1][j] != float('inf') and j >= dist:
                dp[i][j - dist] = min(dp[i][j - dist], dp[i-1][j])

                for k in range(401 - (j - dist)):
                    dp[i][j - dist + k] = min(dp[i][j - dist + k], dp[i][j - dist] + k * price)

                stop = False

        if stop:
            return -1

    return  -1


if __name__ == "__main__":
    # 测试样例
    gas_stations1 = [(100, 1), (200, 30), (400, 40), (300, 20)]
    gas_stations2 = [(300, 25), (600, 35), (900, 5)]
    gas_stations3 = [(100, 50), (150, 45)]
    gas_stations4 = [(100, 10), (200, 20), (300, 30), (400, 40), (600, 15)]
    gas_stations5 = [(25, 100)]

    print(solution(500, 4, gas_stations1) == 4300)
    print(solution(1000, 3, gas_stations2) == -1)
    print(solution(200, 2, gas_stations3) == 9000)
    print(solution(700, 5, gas_stations4) == 9500)
    print(solution(50, 1, gas_stations5) == 5000)

代码思路

  1. 初始化加油站列表

    • gas_stations 列表的开头和结尾分别添加一个虚拟加油站,距离为0和目标距离,油价为0。这样做的目的是为了简化边界条件的处理。
    • 将加油站列表按照距离进行排序。
  2. 动态规划数组初始化

    • 创建一个二维数组 dp,其中 dp[i][j] 表示到达第 i 个加油站时,油箱剩余 j 升油的最小成本。
    • 初始化 dp[0][200] 为0,表示在起始点油箱剩余200升油的成本为0。
  3. 动态规划状态转移

    • 遍历每个加油站 i,计算从上一个加油站到当前加油站的距离 dist 和当前加油站的油价 price
    • 对于每个可能的油量 j,如果可以从上一个加油站到达当前加油站(即 j >= dist),则更新 dp[i][j - dist]dp[i-1][j]
    • 进一步,对于每个可能的加油量 k,更新 dp[i][j - dist + k]dp[i][j - dist] + k * price,表示在当前加油站加 k 升油的成本。
  4. 边界条件处理

    • 如果到达最后一个加油站时,油量不足以到达目标地点(即 200 + dist > 400),则返回 -1
    • 如果无法从上一个加油站到达当前加油站,则返回 -1
  5. 返回结果

    • 如果能够到达目标地点,返回 dp[n-1][200 + dist],即到达目标地点且油量剩余200升的最小成本。

注意

  • 该算法的时间复杂度为 O(n * 400^2),其中 n 是加油站的数量。
  • 空间复杂度为 O(n * 400),用于存储动态规划数组。