2024华为OD笔试机试 - 寻找最优的路测线路 (python/c++/java D卷C卷真题算法)

华为OD机试(C卷+D卷)2024真题目录(Java & c++ & python)

题目描述

评估一个网络的信号质量,其中一个做法是将网络划分为栅格,然后对每个栅格的信号质量计算。

路测的时候,希望选择一条信号最好的路线(彼此相连的栅格集合)进行演示。

现给出 R 行 C 列的整数数组 Cov,每个单元格的数值 S 即为该栅格的信号质量(已归一化,无单位,值越大信号越好)。

要求从 [0, 0] 到 [R-1, C-1]设计一条最优路测路线。返回该路线得分。

规则:

路测路线可以上下左右四个方向,不能对角
路线的评分是以路线上信号最差的栅格为准的,例如路径 8→4→5→9 的值为4,该线路评分为4。线路最优表示该条线路的评分最高。

输入描述

第一行表示栅格的行数 R

第二行表示栅格的列数 C

第三行开始,每一行表示栅格地图一行的信号值,如5 4 5

输出描述

最优路线的得分

备注

  • 1 ≤ R,C ≤ 20
  • 0 ≤ S ≤ 65535

用例1

输入

3
3
5 4 5
1 2 6
7 4 6

输出

4

说明 路线为:5→4→5→6→6
在这里插入图片描述

用例2

输入

6
5
3 4 6 3 4
0 2 1 1 7
8 8 3 2 7
3 2 4 9 8
4 1 2 0 0
4 6 5 4 3

输出

3

说明 路线为:3→4→6→3→4→7→7→8→9→4→3→8→8→3→4→4→6→5→4→3
在这里插入图片描述

解题思路

求解 起点(0,0) 到 终点(r-1, c-1) 的所有路径中 “最大的” 最小权值节点的权值。

注:每条路径都由至少一个点组成,而每个点都有权值,因此每条路径自身都有一个最小权值节点。比如路径:5→4→5→6→6,其中最小权值节点的权值就是4

本题需要我们找到起点->终点的所有路径各自的最小权值节点,并比较出其中最大的那个。

其实这个问题就是单源最短路径,可以使用Dijkstra算法求解,Dijkstra算法的相关知识如下图:
在这里插入图片描述

在这里插入图片描述


  • 定义一个dist数组:dist[x][y]表示起点(0,0)到终点(x,y)的所有路径中“最大的”最小权值节点的权值

由于dist[x][y]最终记录的是一个最大值,因此我们需要将dist[x][y]初始化为一个最小值,方便后面被更大值替换,由于本题节点(栅格)的权值(信号强度)最小为0,因此这里dist[x][y]可以初始化为0。

初始时,我们将dist数组所有元素都初始化为0,其中dist[0][0] = matrix[0][0],因为起点(0,0)到终点(0,0)的路径中只有一个节点(0,0)。

之后,我们需要定义一个优先队列pq,pq记录路径的终点(x,y),各路径终点的优先级为:对应路径"最大的"最小权值节点的权值,即dist[x][y],即dist[x][y]越大,则对应路径终点(x,y)在优先队列中的优先级越高。

初始时,将(0,0)加入优先队列。

下面开始从优先队列不停取出优先级最高的节点:

每当从优先队列中取出一个路径终点(优先级最高),则可以获得如下信息:

  • 路径终点的位置 (ux, uy)
  • 路径的最小权值节点的权值:dist[ux][uy]

之后基于(ux, uy) 向上下左右四个方向探索,如果新位置(vx, vy)不越界,则进入新位置:

  • 此时路径终点从(ux, uy)变更为(vx, vy)
  • 此时路径的最小权值节点的权值w为:min(dist[ux][uy], matrix[vx][vy])

我们得到了一个新的路径,新路径的终点为(vx, vy),新路径中最小权值节点的权值w为 min(dist[ux][uy], matrix[vx][vy])

另外,如果 w > dist[vx][vy],则说明我们找到了起点(0,0)到(vx, vy)的更优路径,即找到了更大的最小权值节点,因此需要更新 dist[vx][vy] = w,然后将新路径加入到pq中重新排优先级。

按此逻辑一直进行,直到pq为空时,我们就找完了起点(0,0)到所有节点的路径的"最大的"最小权值节点的权值。

最后返回 dist[r-1][c-1] 记录的 起点(0,0) 到 (r-1, c-1) 的所有路径中的"最大的"最小权值节点的权值即可

C++、Java、Python代码如下:

C++参考代码

#include <bits/stdc++.h>
using namespace std;

int main() {
    int rows, cols;
    cin >> rows >> cols;

    vector<vector<int>> matrix(rows, vector<int>(cols));
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            cin >> matrix[i][j];
        }
    }

    // dist[i] 记录的是起点(0,0)到终点(i,j)的所有路径中,"最大的"最小权值节点的权值
    vector<int> dist(rows * cols, 0);
    
    // 起点(0,0)的最小权值节点即为自身
    dist[0] = matrix[0][0];

    // 优先队列,按路径中最小权值节点的权值从大到小排序
    auto cmp = [&dist](int a, int b) {
        return dist[a] < dist[b];
    };

    priority_queue<int, vector<int>, decltype(cmp)> pq(cmp);
    pq.push(0);

    // 方向数组,表示上下左右四个方向的移动
    vector<pair<int, int>> directions = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};

    while (!pq.empty()) {
        int u = pq.top();
        pq.pop();

        int x = u / cols;
        int y = u % cols;

        // 如果已经到达终点,直接结束
        if (x == rows - 1 && y == cols - 1) break;

        for (auto& dir : directions) {
            int newX = x + dir.first;
            int newY = y + dir.second;

            if (newX < 0 || newX >= rows || newY < 0 || newY >= cols) continue;

            int v = newX * cols + newY;
            int newMinValue = min(dist[u], matrix[newX][newY]);

            if (dist[v] < newMinValue) {
                dist[v] = newMinValue;
                pq.push(v);
            }
        }
    }

    // 输出终点处的“最大的”最小权值节点的权值
    cout << dist[rows * cols - 1] << endl;

    return 0;
}

Java参考代码

import java.util.PriorityQueue;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int rows = sc.nextInt();
        int cols = sc.nextInt();

        int[][] matrix = new int[rows][cols];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                matrix[i][j] = sc.nextInt();
            }
        }

        // dist[i]记录从起点(0,0)到终点(i/c, i%c)的所有路径中“最大的”最小权值节点的权值
        int[] dist = new int[rows * cols];
        dist[0] = matrix[0][0];

        // 优先队列,路径中最小权值越大优先级越高
        PriorityQueue<Integer> pq = new PriorityQueue<>((a, b) -> dist[b] - dist[a]);
        pq.add(0);

        int[][] directions = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};

        while (!pq.isEmpty()) {
            int current = pq.poll();

            int x = current / cols;
            int y = current % cols;

            // 如果已到达终点,结束循环
            if (x == rows - 1 && y == cols - 1) break;

            for (int[] dir : directions) {
                int newX = x + dir[0];
                int newY = y + dir[1];

                if (newX < 0 || newX >= rows || newY < 0 || newY >= cols) continue;

                int next = newX * cols + newY;
                int minWeight = Math.min(dist[current], matrix[newX][newY]);

                if (dist[next] < minWeight) {
                    dist[next] = minWeight;
                    pq.add(next);
                }
            }
        }

        // 输出起点到终点路径中"最大的"最小权值节点的权值
        System.out.println(dist[rows * cols - 1]);
    }
}

Python参考代码

# 输入获取
r = int(input())
c = int(input())
matrix = [list(map(int, input().split())) for _ in range(r)]

# dist[i]记录从起点0到终点i路径中“最大的”最小权值节点的权值
dist = [0] * (r * c)
dist[0] = matrix[0][0]

# 优先队列初始化,记录路径终点,最小权值节点的权值越大,优先级越高
pq = [0]

# 上下左右的方向偏移量
offsets = [(-1, 0), (1, 0), (0, -1), (0, 1)]

while pq:
    # 取出优先队列中优先级最大的路径(终点)
    u = pq.pop()

    # 将一维化坐标u,解析为二维坐标(x, y)
    x, y = divmod(u, c)

    # 如果已找到终点(r-1, c-1)的最优解,可以提前结束
    if x == r - 1 and y == c - 1:
        break

    # 向上下左右四个方向探索
    for offsetX, offsetY in offsets:
        newX, newY = x + offsetX, y + offsetY

        # 检查新位置是否越界
        if 0 <= newX < r and 0 <= newY < c:
            v = newX * c + newY
            w = min(dist[u], matrix[newX][newY])

            # 如果形成的新路径的最小权值节点更大,则更新dist[v]
            if dist[v] < w:
                dist[v] = w
                pq.append(v)
                pq.sort(key=lambda i: dist[i])  # 升序排序,尾部优先级最大

# 输出起点到终点的所有路径中"最大的"最小权值节点的权值
print(dist[r * c - 1])

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

算法之旅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值