3-7 汽车加油行驶问题
问题描述
给定一个NxN的方形网格,设其左上角为起点O,坐标为(1,1),X轴向右为正.y轴向下为正,每个方格边长为1。一辆汽车从起点0出发驶向右下角终点▲,其坐标为(N,N)。在若干网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在行驶过程应遵守如下规则:
(1)汽车只能沿网格边行驶,装满油后能行驶K条网格边。出发时汽车已装满油,在起点和终点不设油库
(2)当汽车行驶经过一条网格边时,若其X坐标或坐标减小,则应付费用B,否则免付费用
(3)汽车在行驶过程中遇油库则应加满油并付加油费用A
(4)在需要时可在网格点处增设油库,并付增设油库费用C(不含加油费用A)
(5)N,K,A,B,C均为整数
算法设计
求出汽车从起点到终点的一条付费最小的行驶路线
数据输入
由文件input.txt提供输入数据。文件的第1行是N,K,A,B,C的值,2<N<100,2<K<10。第2行起是一个NxN的0-1方阵,每行N个值,至N+1行结束。方阵的第i行第广列处的值为1表示在网格交叉点(i,j)处设置了一个油库,为0时表示未设油库。各行相邻的2个数以空格分隔。
结果输出
将找到的最优行驶路线所需的费用即最小费用输出到文件output.txt。文件的第1行中的数是最小费用值。
示例输入 输出示例
9 3 2 3 6 12
0 0 0 0 1 0 0 0 0
0 0 0 1 0 1 1 0 0
1 0 1 0 0 0 0 1 0
0 0 0 0 0 1 0 0 1
1 0 0 1 0 0 1 0 0
0 1 0 0 0 0 0 1 0
0 0 0 0 1 0 0 0 1
1 0 0 1 0 0 0 1 0
0 1 0 0 0 0 0 0 0
算法实现与思路分析
- 回溯法(DFS+剪枝)
核心思路
递归探索:递归遍历所有可能的路径,记录每条路径的费用
剪枝优化:若当前费用≥已知最小费用,或当前费用已经不是最优(高于dp值,终止递归)
核心代码


性能分析
时间复杂度:最坏情况下非常大,可超过指数级,尽管我们进行了剪枝,效果依然比较差。
空间复杂度:O(N+K)(递归深度:最短路径是2N-2步(全向右和向下),最长路径(如绕路)不会超过O(N²);而每次满油,也最多行驶K步,当油耗尽后会重新递归,更新递归深度),考虑dp数组则为O(N²K)
- BFS(队列+dp)
核心思路
用(x,y,r)和dp[x][y][r]记录状态和费用
按照先入先出的顺序处理,(相当于对树结构进行层次遍历)
状态转移时通过dp数组排除高费用状态
核心代码


性能分析
时间复杂度:最坏情况下O (N²・K × 2^(N+K))(指数级)。由于队列无序,同一状态可能被多次加入(费用递增),无效操作极多:
网格大小为N×N,油量上限为K,则:
- 状态总数:最多为N×N×(K+1)(每个坐标N×N,油量0~K)。
- 每个状态的入队次数:在最坏情况下,每个状态可能被指数级次数入队。例如,若每次到达同一状态的费用都比之前低(但 BFS仍会处理所有历史状态),入队次数可能达到O(2^(N+K))(随路径长度指数增长)。
- 每次入队的处理成本:每个状态需要尝试 4 个方向的移动(O(1)操作)。
因此,总时间复杂度为:状态总数 × 平均入队次数 × 单次处理成本 ≈ O (N²・K × 2^(N+K)),这是一个指数级复杂度。
空间复杂度:dp数组O(N²K)
- Dijkstra(优先队列+dp)
核心思路
同理用状态表示,但是我们将队列换为优先队列,可以避免许多高费用状态的重复访问,更快得到结果
我们在此基础上还有一步优化:在遍历时考虑到,我们其实应该是尽量避免新建加油站的(一方面加油次数要尽可能少,因为每次加油不论剩余油量耗费是一样的,另一方面由于每次路过加油站一定要加油,故新建加油站是非常不划算的),所以在剩余油量达到0之前我们都可以避免新建加油站。
核心代码

该部分是状态结构体,并且定义了优先队列按最小堆来实现


性能分析
时间复杂度:O (N²・K・log (N²・K))。状态总数为 N²・K(每个坐标 N×N,油量K+1个状态),优先队列每次操作复杂度为 log (状态总数),每个状态扩展 4 个方向(O (1))。
空间复杂度: dp数组O(N²K)
测试
对参考测试样例进行测(比较三者的速率)
467

被折叠的 条评论
为什么被折叠?



