华为OD(C卷,200分)- 智能驾驶
(C卷,200分)- 智能驾驶
题目描述
有一辆汽车需要从 m * n 的地图左上角出发,前往地图的右下角。该地图中的每个地区都有一定的油量消耗,地图上的加油站可以加满油。请计算汽车从起点到达终点所需的最少初始油量。说明:汽车可以在上下左右四个方向移动;地图上的数字取值代表:-1 为加油站,可以加满油,汽车的油箱容量最大为100;0 为障碍物,汽车不能通过;正整数代表汽车经过该地区时的耗油量。如果汽车无法到达终点,则返回 -1。
输入描述
第一行包含两个整数 M 和 N,表示地图的总单元格数为 M×N。其中 M 和 N 均满足 0 < M, N ≤ 200。接下来将提供一个大小为 M×N 的矩阵,其中每个元素的取值范围为 0、-1 或正整数。此外,整个矩阵中加油站的总数不超过 200 个。
输出描述
无论怎样操作,汽车无法抵达终点时,返回 -1。如果汽车能够抵达终点,则返回最小的初始油量。
用例
输入 2,2
10,20
30,40
输出 70
说明 行走的路线为:右→下
输入 4,4
10,30,30,20
30,30,-1,10
0,20,20,40
10,-1,30,40
输出 70
说明 行走的路线为:右→右→下→下→下→右
输入 4,5
10,0,30,-1,10
30,0,20,0,20
10,0,10,0,30
10,-1,30,0,10
输出 60
说明 行走的路线为:下→下→下→右→右→上→上→上→右→右→下→下→下
输入 4,4
10,30,30,20
30,30,20,10
10,20,10,40
10,20,30,40
输出 -1
说明 无论如何都无法到达终点
题目解析
用例1路径

用例2路径

用例3路径

考虑到汽车油箱的最大容量为100单位,即使在初始状态下加满油箱,也存在无法从起点到达终点的特定情况。特殊案例中,坐标点3,3对应油箱容量为10,油量为-1,油箱容量为40的特殊配置。具体路线图如下图所示,建议参考下图中的红色箭头路径,之后沿绿色箭头路径行驶。

按照规划的路线,从左上角到右下角仅需10个油量即可完成。这种行驶路线既符合实际需求,又满足本题的要求。在实际生活中,当车辆油量接近耗尽时,会优先选择就近的加油站进行补给,即使加油站不在预设的行驶路线内。
代码思路
主要通过以下几个步骤来计算汽车使用的最小汽油量:
初始化起点位置的状态:
当起始位置为加油站时,将初始油量设为0,剩余油量设为100,并标记为已加油状态。
否则,起始油量设为其位置的油耗值,剩余油量设为0,并标记为未加油状态。
在进行BFS遍历的过程中,对于每一个未被访问的新节点,需要确定从当前位置到该节点所需的最小初始油量值以及剩余油量。
当位置为加油站时,剩余油量将被设置为100,并标记为已加油;否则,将该位置的油耗值从当前剩余油量中扣除。如果扣除后剩余油量为负数且之前未进行过加油操作,则需要从初始油量中借油,即初始油量需要增加(-remain)的值。如果初始油量超过100,则表示无法到达该位置,需要放弃探索。
在更新每个位置的最小初始油量和最大剩余油量的过程中,会对当前路径的状态与历史最优状态进行对比分析。
当当前路径的初始油量更低,或者初始油量相同但剩余油量更高时,更新为当前路径的状态。
计算得出,dist_init[m-1][n-1] 是从起点到终点所需的最小初始油量。当无法从起点到达终点时,返回值为 -1。
该算法通过BFS方式,实时计算从起始点到每个位置所需的最小初始油量和最大剩余油量,并在遇到加油站时,会将剩余油量补充至满油状态。这样可以确保从起点到终点所消耗的汽油量最少。
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define MAX_SIZE 200
typedef struct {
int x; // 位置横坐标
int y; // 位置纵坐标
int init; // 到达此位置所需的最少初始油量
int remain; // 到达此位置时剩余可用油量
int flag; // 到达此位置前有没有加过油
} Node;
// m * n 的地图
int m, n;
// 地图矩阵
int matrix[MAX_SIZE][MAX_SIZE];
// 上下左右四个方向对应的偏移量
int offsets[4][2] = {{-1, 0},
{1, 0},
{0, -1},
{0, 1}};
int bfs() {
// 如果左上角和右下角不可达,则直接返回-1
if (matrix[0][0] == 0 || matrix[m - 1][n - 1] == 0) {
return -1;
}
// 广搜队列
int head = 0;
int tail = 0;
Node queue[MAX_SIZE * MAX_SIZE];
// 起始位置
Node src;
src.x = 0;
src.y = 0;
if (matrix[0][0] == -1) {
// 如果起始位置就是加油站,则到达(0,0)位置所需初始油量为0,且剩余可用油量为100,且需要标记已加油
src.init = 0;
src.remain = 100;
src.flag = 1;
} else {
// 如果起始位置不是加油站,则到达(0,0)位置所需的初始油量至少为matrix[0][0], 剩余可用油量为0,未加油状态
src.init = matrix[0][0];
src.remain = 0;
src.flag = 0;
}
// 将起始位置加入队列
queue[tail++] = src;
// dist_init[x][y] 用于记录起点 (0, 0) 到达 (x, y) 的所有可达路径中最优路径(即初始油量需求最少的路径)的初始油量
int dist_init[MAX_SIZE][MAX_SIZE];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
// 由于需要记录每个位置的最少需要的初始油量,因此每个位置所需的初始油量初始化为一个较大值
dist_init[i][j] = INT_MAX;
}
}
// dist_remain 用于记录起点 (0,0) 到达 (x,y) 的所有可达路径中最优路径(即初始油量需求最少的路径)的最大剩余可用油量
// 即如果存在多条最优路径,我们应该选这些路径中到达此位置剩余油量最多的
int dist_remain[MAX_SIZE][MAX_SIZE] = {0};
// 起点(0,0)到达自身位置(0,0)所需的最少初始油量和最多剩余油量
dist_init[0][0] = src.init;
dist_remain[0][0] = src.remain;
// 广搜
while (head != tail) {
Node cur = queue[head++];
// 从当前位置cur开始向上下左右四个方向探路
for (int i = 0; i < 4; i++) {
// 新位置
int newX = cur.x + offsets[i][0];
int newY = cur.y + offsets[i][1];
// 新位置越界 或者 新位置是障碍,则新位置不可达,继续探索其他方向
if (newX < 0 || newX >= m || newY < 0 || newY >= n || matrix[newX][newY] == 0) {
continue;
}
// 如果新位置可达,则计算到达新位置的三个状态数据
int init = cur.init; // 到达新位置所需的最少初始油量
int remain = cur.remain; // 到达新位置时还剩余的最多可用油量
int flag = cur.flag; // 是否加油了
if (matrix[newX][newY] == -1) {
// 如果新位置是加油站,则加满油
remain = 100;
// 标记加过油了
flag = 1;
} else {
// 如果新位置不是加油站,则需要消耗matrix[newX][newY]个油
remain -= matrix[newX][newY];
}
// 如果到达新位置后,剩余油量为负数
if (remain < 0) {
if (flag) {
// 如果之前已经加过油了,则说明到达此路径前是满油状态,因此我们无法从初始油量里面"借"油
continue;
} else {
// 如果之前没有加过油,则超出的油量(-remain),可以从初始油量里面"借",即需要初始油量 init + (-remain) 才能到达新位置
init -= remain;
// 由于初始油量 init + (-remain) 刚好只能支持汽车到达新位置,因此汽车到达新位置后剩余可用油量为0
remain = 0;
}
}
// 如果到达新位置所需的初始油量超过了满油100,则无法到达新位置
if (init > 100) {
continue;
}
// 如果可达新位置,则继续检查当前路径策略到达新位置(newX, newY)所需的初始油量init是否比其他路径策略更少
if (init > dist_init[newX][newY]) {
// 如果不是,则无需探索新位置(newX, newY)
continue;
}
// 当前路径策略到达新位置(newX,newY)所需初始油量init更少,或者,init和前面路径策略相同,但是当前路径策略剩余可用油量remain更多
if (init < dist_init[newX][newY] || remain > dist_remain[newX][newY]) {
// 则当前路径策略更优,记录更优路径的状态
dist_init[newX][newY] = init;
dist_remain[newX][newY] = remain;
// 将当前新位置加入BFS队列
queue[tail++] = (Node) {newX, newY, init, remain, flag};
}
}
}
// dist_init[m - 1][n - 1] 记录的是到达右下角终点位置所需的最少初始油量
if (dist_init[m - 1][n - 1] == INT_MAX) {
return -1;
} else {
return dist_init[m - 1][n - 1];
}
}
int main() {
scanf("%d,%d", &m, &n);
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
scanf("%d", &matrix[i][j]);
getchar();
}
}
printf("%d\n", bfs());
return 0;
}
