Advertisement

华为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;
    }

全部评论 (0)

还没有任何评论哟~