2026华为OD面试题002:路口最短时间问题

题目描述

给一个 n * m 的棋盘,每个街口有个红绿灯周期 T。车辆通过每段街道需要 timePerRoad 时间。

在街口想直行或左转,得等一个完整的红灯周期 T;右转不用等。起点和终点不算红绿灯时间,可以任意方向通过。

求从起点 (rowStart, colStart) 到终点 (rowEnd, colEnd) 的最短时间。如果走不通,返回 -1。

nm 都不超过 9,timePerRoad 最大 600,红绿灯周期最大 120。

讲个故事:小明的导航失灵了

小明开车去赴约,结果导航抽风,只告诉他每个路口红灯多久。他发现:

  • 直走或左转,得乖乖等红灯
  • 右转美滋滋,不用等
  • 起点和终点随便走,没人管

这不就是现实版"能右拐绝不左转"吗?

核心原理:为什么不是普通 BFS?

如果只是找最短路径,BFS 就行。但每个路口的"等待时间"不一样,而且能不能走取决于你从哪个方向来、往哪个方向去

所以普通坐标不够,得把状态扩成三元组:(行, 列, 朝向)。朝向表示你到达这个路口时面朝哪个方向。

从状态 (r, c, dir) 出发,下一步有三种选择:

  • 直行:继续朝 dir 走,要等待
  • 左转:朝左边走,要等待
  • 右转:朝右边走,不用等待

起点可以任意朝向出发,且不用等。到达终点直接结束,也不用等。

这就是带状态的 Dijkstra。

怎么实现?

  1. 定义四个方向:上、右、下、左
  2. 左转 = (dir + 3) % 4,右转 = (dir + 1) % 4,直行 = dir
  3. 状态 (r, c, dir) 的代价是到达 (r, c) 且面朝 dir 的最短时间
  4. 用优先队列跑 Dijkstra
  5. 第一次弹出终点坐标时,就是答案

代码实现

C 语言

c 复制代码
#include <stdio.h>
#include <string.h>

#define INF 0x3f3f3f3f

int calcTime(int lights[9][9], int n, int m, int timePerRoad,
             int rs, int cs, int re, int ce) {
    int dr[4] = {-1, 0, 1, 0};
    int dc[4] = {0, 1, 0, -1};
    int dist[9][9][4];
    int used[9][9][4] = {0};
    memset(dist, 0x3f, sizeof(dist));

    for (int d = 0; d < 4; d++) dist[rs][cs][d] = 0;

    while (1) {
        int mr = -1, mc = -1, md = -1, minCost = INF;
        for (int r = 0; r < n; r++)
            for (int c = 0; c < m; c++)
                for (int d = 0; d < 4; d++)
                    if (!used[r][c][d] && dist[r][c][d] < minCost) {
                        minCost = dist[r][c][d];
                        mr = r; mc = c; md = d;
                    }
        if (mr == -1) break;
        if (mr == re && mc == ce) return minCost;
        used[mr][mc][md] = 1;

        for (int turn = 0; turn < 3; turn++) {
            int ndir = md;
            if (turn == 1) ndir = (md + 3) % 4;      // 左转
            else if (turn == 2) ndir = (md + 1) % 4; // 右转

            int nr = mr + dr[ndir], nc = mc + dc[ndir];
            if (nr < 0 || nr >= n || nc < 0 || nc >= m) continue;

            int wait = 0;
            if (!(mr == rs && mc == cs)) {
                if (ndir == md || ndir == (md + 3) % 4)
                    wait = lights[mr][mc];
            }

            int ncost = minCost + wait + timePerRoad;
            if (ncost < dist[nr][nc][ndir])
                dist[nr][nc][ndir] = ncost;
        }
    }

    int ans = INF;
    for (int d = 0; d < 4; d++)
        ans = ans < dist[re][ce][d] ? ans : dist[re][ce][d];
    return ans == INF ? -1 : ans;
}

C++

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int calcTime(vector<vector<int>> lights, int timePerRoad,
             int rs, int cs, int re, int ce) {
    int n = lights.size(), m = lights[0].size();
    int dr[4] = {-1, 0, 1, 0};
    int dc[4] = {0, 1, 0, -1};
    const int INF = 0x3f3f3f3f;
    vector<vector<array<int, 4>>> dist(n, vector<array<int, 4>>(m, {INF, INF, INF, INF}));
    priority_queue<pair<int, array<int, 3>>, vector<pair<int, array<int, 3>>>, greater<>> pq;

    for (int d = 0; d < 4; d++) {
        dist[rs][cs][d] = 0;
        pq.push({0, {rs, cs, d}});
    }

    while (!pq.empty()) {
        auto [cost, state] = pq.top(); pq.pop();
        auto [r, c, dir] = state;
        if (r == re && c == ce) return cost;
        if (cost > dist[r][c][dir]) continue;

        for (int turn = 0; turn < 3; turn++) {
            int ndir = dir;
            if (turn == 1) ndir = (dir + 3) % 4;
            else if (turn == 2) ndir = (dir + 1) % 4;

            int nr = r + dr[ndir], nc = c + dc[ndir];
            if (nr < 0 || nr >= n || nc < 0 || nc >= m) continue;

            int wait = 0;
            if (!(r == rs && c == cs)) {
                if (ndir == dir || ndir == (dir + 3) % 4)
                    wait = lights[r][c];
            }

            int ncost = cost + wait + timePerRoad;
            if (ncost < dist[nr][nc][ndir]) {
                dist[nr][nc][ndir] = ncost;
                pq.push({ncost, {nr, nc, ndir}});
            }
        }
    }

    int ans = INF;
    for (int d = 0; d < 4; d++) ans = min(ans, dist[re][ce][d]);
    return ans == INF ? -1 : ans;
}

Java

java 复制代码
import java.util.PriorityQueue;

public class Main {
    static int INF = 0x3f3f3f3f;
    static int[] dr = {-1, 0, 1, 0};
    static int[] dc = {0, 1, 0, -1};

    static class Node implements Comparable<Node> {
        int r, c, dir, cost;
        Node(int r, int c, int dir, int cost) {
            this.r = r; this.c = c; this.dir = dir; this.cost = cost;
        }
        public int compareTo(Node o) {
            return this.cost - o.cost;
        }
    }

    public static int calcTime(int[][] lights, int timePerRoad,
                                int rs, int cs, int re, int ce) {
        int n = lights.length, m = lights[0].length;
        int[][][] dist = new int[n][m][4];
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                for (int k = 0; k < 4; k++)
                    dist[i][j][k] = INF;

        PriorityQueue<Node> pq = new PriorityQueue<>();
        for (int d = 0; d < 4; d++) {
            dist[rs][cs][d] = 0;
            pq.offer(new Node(rs, cs, d, 0));
        }

        while (!pq.isEmpty()) {
            Node cur = pq.poll();
            if (cur.r == re && cur.c == ce) return cur.cost;
            if (cur.cost > dist[cur.r][cur.c][cur.dir]) continue;

            for (int turn = 0; turn < 3; turn++) {
                int ndir = cur.dir;
                if (turn == 1) ndir = (cur.dir + 3) % 4;
                else if (turn == 2) ndir = (cur.dir + 1) % 4;

                int nr = cur.r + dr[ndir], nc = cur.c + dc[ndir];
                if (nr < 0 || nr >= n || nc < 0 || nc >= m) continue;

                int wait = 0;
                if (!(cur.r == rs && cur.c == cs)) {
                    if (ndir == cur.dir || ndir == (cur.dir + 3) % 4)
                        wait = lights[cur.r][cur.c];
                }

                int ncost = cur.cost + wait + timePerRoad;
                if (ncost < dist[nr][nc][ndir]) {
                    dist[nr][nc][ndir] = ncost;
                    pq.offer(new Node(nr, nc, ndir, ncost));
                }
            }
        }

        int ans = INF;
        for (int d = 0; d < 4; d++) ans = Math.min(ans, dist[re][ce][d]);
        return ans == INF ? -1 : ans;
    }
}

JavaScript

javascript 复制代码
function calcTime(lights, timePerRoad, rs, cs, re, ce) {
    const n = lights.length, m = lights[0].length;
    const dr = [-1, 0, 1, 0];
    const dc = [0, 1, 0, -1];
    const INF = 0x3f3f3f3f;
    const dist = Array.from({length: n}, () =>
        Array.from({length: m}, () => new Array(4).fill(INF)));
    const pq = [];

    const push = (node) => {
        pq.push(node);
        pq.sort((a, b) => a.cost - b.cost);
    };

    for (let d = 0; d < 4; d++) {
        dist[rs][cs][d] = 0;
        push({r: rs, c: cs, dir: d, cost: 0});
    }

    while (pq.length > 0) {
        const cur = pq.shift();
        if (cur.r === re && cur.c === ce) return cur.cost;
        if (cur.cost > dist[cur.r][cur.c][cur.dir]) continue;

        for (let turn = 0; turn < 3; turn++) {
            let ndir = cur.dir;
            if (turn === 1) ndir = (cur.dir + 3) % 4;
            else if (turn === 2) ndir = (cur.dir + 1) % 4;

            const nr = cur.r + dr[ndir], nc = cur.c + dc[ndir];
            if (nr < 0 || nr >= n || nc < 0 || nc >= m) continue;

            let wait = 0;
            if (!(cur.r === rs && cur.c === cs)) {
                if (ndir === cur.dir || ndir === (cur.dir + 3) % 4)
                    wait = lights[cur.r][cur.c];
            }

            const ncost = cur.cost + wait + timePerRoad;
            if (ncost < dist[nr][nc][ndir]) {
                dist[nr][nc][ndir] = ncost;
                push({r: nr, c: nc, dir: ndir, cost: ncost});
            }
        }
    }

    let ans = INF;
    for (let d = 0; d < 4; d++) ans = Math.min(ans, dist[re][ce][d]);
    return ans === INF ? -1 : ans;
}

Python

python 复制代码
import heapq

def calc_time(lights, time_per_road, rs, cs, re, ce):
    n, m = len(lights), len(lights[0])
    dr = [-1, 0, 1, 0]
    dc = [0, 1, 0, -1]
    INF = 0x3f3f3f3f
    dist = [[[INF] * 4 for _ in range(m)] for _ in range(n)]
    pq = []

    for d in range(4):
        dist[rs][cs][d] = 0
        heapq.heappush(pq, (0, rs, cs, d))

    while pq:
        cost, r, c, dir = heapq.heappop(pq)
        if r == re and c == ce:
            return cost
        if cost > dist[r][c][dir]:
            continue

        for turn in range(3):
            ndir = dir
            if turn == 1:
                ndir = (dir + 3) % 4
            elif turn == 2:
                ndir = (dir + 1) % 4

            nr, nc = r + dr[ndir], c + dc[ndir]
            if not (0 <= nr < n and 0 <= nc < m):
                continue

            wait = 0
            if not (r == rs and c == cs):
                if ndir == dir or ndir == (dir + 3) % 4:
                    wait = lights[r][c]

            ncost = cost + wait + time_per_road
            if ncost < dist[nr][nc][ndir]:
                dist[nr][nc][ndir] = ncost
                heapq.heappush(pq, (ncost, nr, nc, ndir))

    ans = min(dist[re][ce])
    return -1 if ans == INF else ans

复杂度分析

  • 状态数:n * m * 4,最大 9 * 9 * 4 = 324
  • 时间复杂度:O(n * m * 4 * log(n * m * 4)),约等于 O(n * m * log(n * m))
  • 空间复杂度:O(n * m * 4),存距离数组和优先队列

总结一下

这道题的关键是:路口状态不能只看坐标,得加上朝向

(r, c, dir) 当成图上的一个点,用 Dijkstra 跑一遍,右转免费、直行左转等红灯的规则自然就被编码进去了。

下次遇到"路口""红绿灯""转向限制"这种关键词,先把状态升维,大概率就是正解。

你遇到过哪些看起来很复杂、其实升维一下就解决的题?欢迎在评论区聊聊。

相关推荐
令狐掌门8 小时前
2026华为OD面试题001:两个字符串间的最短路径问题
华为od·华为od面试题
2601_9620725518 天前
李梦娇常识4600问|题库|打印版
sql·华为od·华为·c#·华为云·.net·harmonyos
HEADKON21 天前
Larotretinib拉罗替尼治NTRK融合实体瘤,神经系统反应多为一过性
华为od
无限码力1 个月前
2026最新华为OD机试新系统 机考真题考点分类 + 备考策略
华为od·华为od机考·华为od机试·华为od机试新系统真题·华为od机试备考策略·华为od机考真题考点详解
TechPioneer_lp1 个月前
就业指导|中九非科班毕业,华为 OD 做 Java 后端想转 C++,能找到深度学习挂钩的岗工作吗?
java·c++·华为od·华为·就业指导·校招指导
无限码力1 个月前
华为OD机试真题 新系统-等距二进制判断(C/C++/Py/Java/Js/Go)
华为od·华为od机考·华为od机试真题·华为od机试·华为od机试题库·华为od机试新系统真题
largecode2 个月前
怎么让手机显示公司名?来电显示公司名称认证实现品牌外显
linux·ubuntu·华为od·华为·智能手机·华为云·harmonyos
无限码力2 个月前
华为OD新系统机试真题 - 日志文件异常检测
华为od·华为od机考·华为od机试真题·华为od上机考试真题·华为od机考真题·华为od新系统机试真题·华为od新系统机试
无限码力2 个月前
2026最新华为OD新系统机试解析 + 最新题库 + 备考策略
华为od·华为od机考·华为od机试真题·华为od机试·华为od新系统机试真题·2026华为od新系统机试题库·华为od机考题库