【华为OD机考真题】- 矩阵匹配(2025B卷-200分)(Python版)

本文收录于「最新最全华为OD机试真题(Python版)」专栏,手把手带你零基础教学华为OD机试。本题集提供最优题解思路,解题步骤,代码解析,复杂度分析及最优题解源码等,支持多语言题解,助你轻松拿捏OD机考,一举上岸!安利大家关注&&收藏&&订阅题库正在疯狂收录中,up!up!up!!
提醒:拒绝一切代考/替考,违法必究!专栏所写题库均搜集于互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验所撰,欢迎订阅。
  
订阅福利:一次订阅,可永久免费阅读,提供在线答疑解惑,后续题库更新皆可阅读使用!

所有题目均有六种语言实现,汇总如下

  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【全栈版】
  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【Java版】
  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【Python版】
  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【C版】
  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【C++版】
  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【Golang版】
  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【JavaScript版】

如上题库均已同步更新至最新华为OD机试真题,赶紧操练起来吧~~

1. 题目描述

具体题目描述如下:

从一个 N*M (N≤M) 的矩阵中选出 N 个数,任意两个数字不能在同一行或同一列,求选出来的 N 个数中第 K 大的数字的最小
值是多少。

2. 输入描述

输入矩阵要求:1 ≤ K ≤ N ≤ M ≤ 150
输入格式:
N M K
N*M 矩阵

️3. 输出描述

N*M 的矩阵中可以选出 M! / N! 种组合数组,每个组合数组种第 K大的数中的最小值。无需考虑重复数字,直接取 字典排序 结果
即可。
备注:
注意:结果是第K大的数字的最小值。

4. 示例演示

✨4.1 示例1

输入:

3 4 2
1 5 6 6
8 3 4 3
6 8 6 3

输出:

3

示例说明:
N*M的矩阵中可以选出 M!/N!种组合数组,每个组合数组种第 K大的数中的最小值;
上述输入中选出数组组合为:

1,3,6
1,3,3
1,4,8
1,4,3

上述输入样例中选出的组合数组有24种,最小数组为1,3,3,则第2大的最小值为3。

✨4.2 示例2

输入:


输出:


示例说明:

✨4.3 示例3

输入:


输出:


示例说明:

温馨提醒: 大家在参加华为OD机试时,切记不要仅仅死记硬背题解代码。真正的通过率取决于你对代码的理解能力。建议你在理解基本原理和逻辑的基础上,模仿并自己编写代码,这样才能更有效地应对机试。

5. 解题分析

5.1 问题理解

题目要求从一个 N x M 的矩阵中选出 N 个数,要求选出的数字不能在同一行或同一列,并且要求找到这些选出的数字中第 K 大的数字的最小值。需要根据这个问题来构建一种方案,使得能够在所有的合法选择中找到最小的第 K 大值。

  • 输入:

    • N:选择的数字个数。
    • M:矩阵的列数。
    • K:需要找到的第 K 大的数字。
    • 矩阵的 N x M 大小,包含整数值。
  • 输出:

    • 找到的最小的第 K 大的数字。

5.2 解题思路

  1. 题目解构

    • 我们需要从 N x M 的矩阵中选择 N 个数字,且要求这 N 个数字中每个数字必须来自不同的行和列。这样每个选择都可以看作是一个有效的矩阵选择。
    • 选出的 N 个数字中,我们要找出第 K 大的数字中的最小值。
  2. 基本方法

    • 二分查找:根据题意,我们可以通过二分查找来确定一个数字 x,然后通过构建一个图来检查是否可以在矩阵中选出 N 个不同行、不同列的数字,使得这些数字都小于或等于 x
    • 最大匹配:利用二分查找的过程中,对于每个 x,通过构建一个二分图(bipartite graph),通过图的最大匹配来判断是否能选出 N 个数字满足条件。具体地,我们通过DFS(深度优先搜索)来实现图中的匹配操作。
  3. 详细步骤

    • 对于每个可能的 x,构建一个二分图。图的每个节点表示矩阵中的行或列,如果某个数字小于等于 x,则我们在该数字所在的行和列之间建立一条边。
    • 通过最大匹配算法(如DFS)来判断是否能够在图中找到一个匹配,使得可以选出 N 个不同行、不同列的数字。
    • 通过二分查找来逐步缩小 x 的范围,最终找到最小的 K 大数字。
  4. 二分查找的收敛

    • 二分查找的上下界分别为 1 和矩阵中的最大值。通过反复测试中间值 mid,判断是否可以选出 N 个数,如果可以则更新结果,并缩小搜索区间。

5.3 问题考点

  1. 二分查找:利用二分查找技术高效地找到目标数字。
  2. 最大匹配算法:通过二分图和最大匹配判断是否能从矩阵中选出合适的 N 个数。
  3. 图论:理解如何构建二分图并使用DFS来寻找最大匹配。
  4. 复杂度分析:通过优化方法(如二分查找和最大匹配)保证算法在大规模输入下的高效性。

5.4 解题步骤

  1. 读取输入数据

    • 首先读取 NMK 的值,然后读取矩阵数据。
  2. 二分查找初始化

    • 设置二分查找的左右边界:l = 1r = max(matrix),其中 max(matrix) 是矩阵中所有元素的最大值。
  3. 构建二分图和最大匹配

    • 对于每一个 mid,构建一个二分图,其中图的边表示矩阵中哪些行和列的交叉点可以选中。
    • 使用DFS来判断是否可以在图中找到一个匹配,使得选出的 N 个数的值都小于等于 mid
  4. 二分查找核心逻辑

    • 在二分查找过程中,通过调用最大匹配方法来判断当前的 mid 是否满足条件。如果满足,则可能存在更小的解,将 r 设置为 mid - 1,继续缩小搜索范围。否则,增大 mid,继续查找更大的解。
  5. 返回结果

    • 最终通过二分查找找到最小的第 K 大数字,输出结果。

6. 解题Coding

  根据如上题解思路,进行代码实战,大家请看如下,建议不要死记硬背代码,要理解其题型及实现思路,别担心,代码我都会给出超详细注释,你一定能看明白的。

✅6.1 代码实现(Python版)

class MatrixMatching:
    def __init__(self, n, m, k, a):
        self.n = n  # 行数
        self.m = m  # 列数
        self.k = k  # 最大不匹配数量
        self.V = 0
        self.match = [-1] * 1000
        self.used = [0] * 1000
        self.Graph = [[] for _ in range(1000)]  # 创建图

        self.a = a  # 输入的矩阵

    def add_edge(self, u, v):
        self.Graph[u].append(v)
        self.Graph[v].append(u)

    def dfs(self, v):
        self.used[v] = 1
        for u in self.Graph[v]:
            w = self.match[u]
            if w < 0 or (self.used[w] == 0 and self.dfs(w) == 1):
                self.match[v] = u
                self.match[u] = v
                return 1
        return 0

    def bipartite_matching(self):
        res = 0
        for i in range(len(self.match)):
            self.match[i] = -1
        for v in range(self.V):
            if self.match[v] < 0:
                self.used = [0] * 1000  # 重置used数组
                if self.dfs(v) == 1:
                    res += 1
        return res

    def check(self, x):
        self.V = self.n + self.m + 1
        for i in range(self.V):
            self.Graph[i].clear()
        for i in range(1, self.n + 1):
            for j in range(1, self.m + 1):
                if self.a[i][j] <= x:
                    self.add_edge(i, self.n + j)
        return self.bipartite_matching() >= self.n - self.k + 1

    def solve(self):
        l, r = 1, 1000000000
        ans = r
        while l <= r:
            mid = (l + r) >> 1
            if self.check(mid):
                r = mid - 1
                ans = mid
            else:
                l = mid + 1
        return ans


if __name__ == "__main__":
    n, m, k = map(int, input().split())  # 输入n, m, k
    a = [list(map(int, input().split())) for _ in range(n + 1)]  # 输入矩阵a

    matrix_matching = MatrixMatching(n, m, k, a)
    result = matrix_matching.solve()
    print(result)  # 输出结果

⏱6.2 时间&空间复杂度

时间复杂度

  • 二分查找部分的时间复杂度是 O(log(maxValue)),其中 maxValue 是矩阵元素的最大值,即 1,000,000,000。
  • 对于每次二分查找的检查函数 check,需要运行 bipartite_matching 来检查最大匹配。bipartite_matching 采用的是深度优先搜索(DFS),在最坏情况下,每个节点需要进行匹配,DFS 的复杂度为 O(V + E),其中 V 是图中顶点的数量,E 是边的数量。对于每次匹配,DFS 最多要遍历整个图。由于图是稠密的,最坏情况下的复杂度为 O(n * m)
  • 因此,总时间复杂度是 O(log(maxValue) * n * m),其中 maxValue = 10^9nm 分别是矩阵的行和列数。

空间复杂度

  • 空间复杂度主要来自图结构存储和辅助数组:
    • Graph 用来存储图的邻接表,空间复杂度是 O(V + E),其中 V 是顶点数,E 是边数。最大情况下,V 是 n + m + 1,而每个节点可能与其他节点都有边,因此最大边数是 n * m
    • 辅助数组 match, used 和矩阵 a 占用的空间为 O(n + m)O(n * m),分别是常数和矩阵大小。
  • 所以,空间复杂度为 O(n * m)

⛓‍6.3 代码解析

  1. 类初始化

    • MatrixMatching 类接受矩阵的大小 nm 和最大不匹配数量 k,并初始化相关的数据结构:match 数组(用于存储匹配结果),used 数组(用于 DFS 遍历时标记节点是否已访问),Graph(邻接表存储图的结构)以及矩阵 a(输入的矩阵)。
  2. 添加边

    • add_edge(u, v) 方法用于在图中添加一条边,表示两个节点之间有连接。
  3. 深度优先搜索(DFS)

    • dfs(v) 方法使用深度优先搜索来尝试为节点 v 寻找匹配。它递归地遍历图中与当前节点相连的节点,寻找未匹配或能够通过“增广路径”匹配的节点。
  4. 二分查找与检查

    • check(x) 方法通过二分查找确定最小的 x 值,使得可以在图中找到 n - k + 1 条匹配的边。它首先清空图的边,然后根据给定的 x 值添加满足条件的边,最后通过最大匹配算法验证是否能找到足够的匹配。
  5. 主程序逻辑

    • solve() 方法实现了二分查找的逻辑,查找最小的 x,使得满足条件的匹配数量达到要求,并返回结果。
  6. 输入输出

    • 输入部分通过 map(int, input().split()) 获取 nmk 的值,并通过嵌套列表解析输入矩阵 a
    • 最后输出最小值 ans,即满足条件的最小 x 值。

6.4 小结

  • 该问题涉及到一个最大匹配算法,通过二分查找和深度优先搜索实现图的匹配。核心思想是通过二分查找最小的边权值 x,使得在这个条件下,能找到足够的匹配。
  • 时间复杂度受二分查找的影响,并且在每次二分查找时运行 O(n * m) 的最大匹配算法。空间复杂度主要由图的存储和辅助数组构成,整体复杂度为 O(n * m)

7. 附录源码(Java版)

  针对如上分享OD机试真题之外,这里我还开源全部OD机试原真题源码,供同学们一对一学习!对照每题都有题目号及详细代码注释。Gitee,例如题序号为1,则题解代码对应文件夹OD1,题序号为5,则题解代码对应文件夹OD5,以此类推,目的就是为了方便大家学习,一举上岸!(这里的题序号指专栏导航贴中表格一列的序号)

福利赠与你

  如果你还想学习更多相关OD真题题解,都建议直接毫不犹豫地学习此专栏「最新最全真题华为OD机试真题(全栈版)」,快速掌握Java、Python、C++、JavaScript、Go等多种热门语言详细解题,快速突破华为OD机试,实现350+高分目标。还将提供线上多端答疑交流,解决你的所有问题!

安利其他语言版本题解册

  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【全栈版】
  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【Java版】
  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【Python版】
  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【C版】
  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【C++版】
  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【Golang版】
  • 【华为OD机试】最新最全真题汇总A+B+C+D+E卷【JavaScript版】

注意: 上述任意专栏一次订阅,获永久免费阅读权限,后续更新都能学习。
声明: 拒绝一切形式的代考,替考行为,务必诚信考试!!!本人所写题库均搜集于互联网。

‍Who am I?

我是bug菌,CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云多年度十佳博主&最具价值贡献奖,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;更多精彩福利点击这里;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿。

-End-

你可能感兴趣的:(华为od,python,华为,华为OD,华为OD机考真题,算法,矩阵匹配)