【2024年华为OD机试】 (C卷,200分)- 矩阵匹配(JavaScriptJava PythonC/C++)

news2025/1/30 12:34:07

在这里插入图片描述

一、问题描述

问题描述

给定一个大小为 ( N \times M )(( N \leq M ))的矩阵,从中选出 ( N ) 个数,要求任意两个数字不能在同一行或同一列。求选出来的 ( N ) 个数中第 ( K ) 大的数字的最小值。

输入描述

  • 输入矩阵要求:( 1 \leq K \leq N \leq M \leq 150 )
  • 输入格式:
    • 第一行:( N ) ( M ) ( K )
    • 接下来 ( N ) 行:( N \times M ) 矩阵

输出描述

  • 输出从矩阵中选出的 ( N ) 个数中第 ( K ) 大的数字的最小值。

用例

输入

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

输出

3

解题思路

1. 问题分析

我们需要从矩阵中选出 ( N ) 个数,且这些数不能在同一行或同一列。然后在这些选出的数中找到第 ( K ) 大的数的最小值。

2. 二分图匹配

  • 将矩阵的行和列分别看作二分图的两部分。
  • 选择 ( N ) 个数且不重复行和列,相当于在二分图中找到一个匹配,匹配的边数至少为 ( N )。
  • 我们需要找到这些匹配中第 ( K ) 大的数的最小值。

3. 二分法

  • 使用二分法来枚举可能的第 ( K ) 大的数 ( kth )。
  • 对于每个枚举的 ( kth ),检查矩阵中是否有至少 ( N - K + 1 ) 个数小于等于 ( kth ),并且这些数不在同一行或同一列。
  • 如果满足条件,则尝试更小的 ( kth );否则,尝试更大的 ( kth )。

4. 二分图最大匹配

  • 对于每个 ( kth ),构建一个新的二分图,其中只包含矩阵中值小于等于 ( kth ) 的元素。
  • 在这个新的二分图中,寻找最大匹配。如果最大匹配数大于等于 ( N - K + 1 ),则说明 ( kth ) 可能是一个候选值。

5. 二分法实现

  • 初始化二分法的左右边界,左边界为矩阵中的最小值,右边界为矩阵中的最大值。
  • 在每次迭代中,计算中间值 ( mid ),并检查是否满足条件。
  • 根据检查结果调整左右边界,直到找到最小的 ( kth )。

总结

通过将问题转化为二分图匹配,并使用二分法来枚举可能的第 ( K ) 大的数,我们可以高效地找到满足条件的最小值。这种方法避免了暴力枚举,大大减少了计算量。

二、JavaScript算法源码

以下是对代码的详细注释和讲解,帮助你理解每一部分的逻辑和实现方式。


代码结构

  1. 输入处理:读取输入的矩阵和参数。
  2. 二分枚举:通过二分法枚举可能的第 ( K ) 大的最小值。
  3. 检查函数:检查当前枚举的值是否满足条件。
  4. DFS 实现二分图匹配:通过深度优先搜索(DFS)实现二分图的最大匹配。

详细注释和讲解

const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;

void (async function () {
  // 读取输入的第一行,解析出 N, M, K
  const [n, m, k] = (await readline()).split(" ").map(Number);

  // 初始化二分枚举的左右边界
  let min = 1; // 最小可能值
  let max = -Infinity; // 最大可能值,初始为负无穷

  // 读取矩阵
  const matrix = [];
  for (let i = 0; i < n; i++) {
    matrix.push((await readline()).split(" ").map(Number)); // 将每一行转换为数字数组
    max = Math.max(max, ...matrix[i]); // 更新最大值
  }

  // 二分枚举第 K 大的最小取值
  while (min <= max) {
    // mid 是被枚举出来的 N 个数中的第 K 大的最小取值
    const mid = (min + max) >> 1; // 取中间值,相当于 Math.floor((min + max) / 2)

    // 检查 mid 是否可以作为 N 个数中第 K 大的值
    if (check(mid)) {
      // 如果满足条件,尝试更小的值
      max = mid - 1;
    } else {
      // 如果不满足条件,尝试更大的值
      min = mid + 1;
    }
  }

  // 输出结果
  console.log(min);

  // 检查函数:判断当前枚举的 kth 是否满足条件
  function check(kth) {
    // 利用二分图最大匹配来求解,小于等于 kth 的元素个数(即二分图最大匹配)
    let smallerCount = 0;

    // 记录每个列号的匹配成功的行号
    // 初始时每个列号都处于未配对状态,此时将列号配对的行号赋值为 -1
    const match = new Array(m).fill(-1);

    // 遍历行号,每个行号对互相心仪的列号发起配对请求
    for (let i = 0; i < n; i++) {
      // 记录增广路访问过的列号
      const vis = new Array(m).fill(false);

      // 如果当前行号 i 可以找到匹配的列号,则 smallerCount + 1
      if (dfs(i, kth, match, vis)) {
        smallerCount++;
      }
    }

    // 判断是否满足条件:小于等于 kth 的元素个数是否 >= N - K + 1
    return smallerCount >= n - k + 1;
  }

  // DFS 实现二分图匹配
  function dfs(i, kth, match, vis) {
    // 行号 i 发起了配对请求

    // 遍历每一个列号 j
    for (let j = 0; j < m; j++) {
      // 如果当前列号 j 未被增广路探索过 && 当前行 i 列 j 的元素值 <= kth
      if (!vis[j] && matrix[i][j] <= kth) {
        vis[j] = true; // 标记列号 j 为已访问

        // 如果列号 j 未配对,或者已配对但可以找到其他行号重新配对
        if (match[j] == -1 || dfs(match[j], kth, match, vis)) {
          // 则当前行号 i 和列号 j 可以配对
          match[j] = i;
          return true;
        }
      }
    }

    // 如果找不到匹配的列号,返回 false
    return false;
  }
})();

代码逻辑详解

1. 输入处理
  • 使用 readline 模块读取输入。
  • 解析第一行得到 ( N )、( M )、( K )。
  • 读取矩阵,并记录矩阵中的最大值 max,作为二分枚举的右边界。
2. 二分枚举
  • 初始化二分枚举的左右边界:
    • min = 1:最小值从 1 开始。
    • max:矩阵中的最大值。
  • 通过二分法枚举可能的第 ( K ) 大的最小值 mid
  • 调用 check(mid) 检查当前 mid 是否满足条件。
3. 检查函数 check(kth)
  • 目标:检查是否存在至少 ( N - K + 1 ) 个小于等于 kth 的元素,并且这些元素不在同一行或同一列。
  • 实现方式:
    • 使用二分图最大匹配算法。
    • 遍历每一行,尝试匹配一个列号。
    • 如果匹配成功,则 smallerCount + 1
    • 最终判断 smallerCount 是否大于等于 ( N - K + 1 )。
4. DFS 实现二分图匹配
  • 目标:为当前行号 i 找到一个匹配的列号 j
  • 实现方式:
    • 遍历所有列号 j
    • 如果列号 j 未被访问过,并且矩阵中 matrix[i][j] <= kth,则尝试匹配。
    • 如果列号 j 未配对,或者已配对但可以找到其他行号重新配对,则匹配成功。
    • 返回匹配结果。
5. 输出结果
  • 当二分枚举结束时,min 即为满足条件的最小值。

关键点总结

  1. 二分枚举:通过二分法高效枚举可能的第 ( K ) 大的最小值。
  2. 二分图匹配:将问题转化为二分图的最大匹配问题,确保选出的数不在同一行或同一列。
  3. DFS 实现匹配:通过 DFS 实现二分图的匹配过程,确保匹配的列号满足条件。

通过以上注释和讲解,你应该能够理解代码的每一部分逻辑和实现方式。如果还有疑问,欢迎随时提出!

三、Java算法源码


代码结构

  1. 输入处理:读取输入的矩阵和参数。
  2. 二分枚举:通过二分法枚举可能的第 ( K ) 大的最小值。
  3. 检查函数:检查当前枚举的值是否满足条件。
  4. DFS 实现二分图匹配:通过深度优先搜索(DFS)实现二分图的最大匹配。

详细注释和讲解

import java.util.Arrays;
import java.util.Scanner;

public class Main {
  // 定义全局变量
  static int n; // 矩阵的行数
  static int m; // 矩阵的列数
  static int k; // 第 K 大
  static int[][] matrix; // 存储矩阵

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

    // 读取输入的第一行,解析出 N, M, K
    n = sc.nextInt();
    m = sc.nextInt();
    k = sc.nextInt();

    // 初始化二分枚举的左右边界
    int min = 1; // 最小可能值
    int max = Integer.MIN_VALUE; // 最大可能值,初始为整型最小值

    // 读取矩阵
    matrix = new int[n][m];
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < m; j++) {
        matrix[i][j] = sc.nextInt(); // 读取矩阵元素
        max = Math.max(max, matrix[i][j]); // 更新最大值
      }
    }

    // 二分枚举第 K 大的最小取值
    while (min <= max) {
      // mid 是被枚举出来的 N 个数中的第 K 大的最小取值
      int mid = (min + max) >> 1; // 取中间值,相当于 (min + max) / 2

      // 检查 mid 是否可以作为 N 个数中第 K 大的值
      if (check(mid)) {
        // 如果满足条件,尝试更小的值
        max = mid - 1;
      } else {
        // 如果不满足条件,尝试更大的值
        min = mid + 1;
      }
    }

    // 输出结果
    System.out.println(min);
  }

  // 检查函数:判断当前枚举的 kth 是否满足条件
  public static boolean check(int kth) {
    // 利用二分图最大匹配来求解,小于等于 kth 的元素个数(即二分图最大匹配)
    int smallerCount = 0;

    // 记录每个列号的匹配成功的行号
    int[] match = new int[m];
    // 初始时每个列号都处于未配对状态,此时将列号配对的行号赋值为 -1
    Arrays.fill(match, -1);

    // 遍历行号,每个行号对互相心仪的列号发起配对请求
    for (int i = 0; i < n; i++) {
      // 记录增广路访问过的列号
      boolean[] vis = new boolean[m];

      // 如果当前行号 i 可以找到匹配的列号,则 smallerCount + 1
      if (dfs(i, kth, match, vis)) {
        smallerCount++;
      }
    }

    // 判断是否满足条件:小于等于 kth 的元素个数是否 >= N - K + 1
    return smallerCount >= n - k + 1;
  }

  // DFS 实现二分图匹配
  public static boolean dfs(int i, int kth, int[] match, boolean[] vis) {
    // 行号 i 发起了配对请求

    // 遍历每一个列号 j
    for (int j = 0; j < m; j++) {
      // 如果当前列号 j 未被增广路探索过 && 当前行 i 列 j 的元素值 <= kth
      if (!vis[j] && matrix[i][j] <= kth) {
        vis[j] = true; // 标记列号 j 为已访问

        // 如果列号 j 未配对,或者已配对但可以找到其他行号重新配对
        if (match[j] == -1 || dfs(match[j], kth, match, vis)) {
          // 则当前行号 i 和列号 j 可以配对
          match[j] = i;
          return true;
        }
      }
    }

    // 如果找不到匹配的列号,返回 false
    return false;
  }
}

代码逻辑详解

1. 输入处理
  • 使用 Scanner 读取输入。
  • 解析第一行得到 ( N )、( M )、( K )。
  • 读取矩阵,并记录矩阵中的最大值 max,作为二分枚举的右边界。
2. 二分枚举
  • 初始化二分枚举的左右边界:
    • min = 1:最小值从 1 开始。
    • max:矩阵中的最大值。
  • 通过二分法枚举可能的第 ( K ) 大的最小值 mid
  • 调用 check(mid) 检查当前 mid 是否满足条件。
3. 检查函数 check(kth)
  • 目标:检查是否存在至少 ( N - K + 1 ) 个小于等于 kth 的元素,并且这些元素不在同一行或同一列。
  • 实现方式:
    • 使用二分图最大匹配算法。
    • 遍历每一行,尝试匹配一个列号。
    • 如果匹配成功,则 smallerCount + 1
    • 最终判断 smallerCount 是否大于等于 ( N - K + 1 )。
4. DFS 实现二分图匹配
  • 目标:为当前行号 i 找到一个匹配的列号 j
  • 实现方式:
    • 遍历所有列号 j
    • 如果列号 j 未被访问过,并且矩阵中 matrix[i][j] <= kth,则尝试匹配。
    • 如果列号 j 未配对,或者已配对但可以找到其他行号重新配对,则匹配成功。
    • 返回匹配结果。
5. 输出结果
  • 当二分枚举结束时,min 即为满足条件的最小值。

关键点总结

  1. 二分枚举:通过二分法高效枚举可能的第 ( K ) 大的最小值。
  2. 二分图匹配:将问题转化为二分图的最大匹配问题,确保选出的数不在同一行或同一列。
  3. DFS 实现匹配:通过 DFS 实现二分图的匹配过程,确保匹配的列号满足条件。

通过以上注释和讲解,你应该能够理解代码的每一部分逻辑和实现方式。如果还有疑问,欢迎随时提出!

四、Python算法源码

以下是对 Python 代码的详细注释和讲解,帮助你理解每一部分的逻辑和实现方式。


代码结构

  1. 输入处理:读取输入的矩阵和参数。
  2. 二分枚举:通过二分法枚举可能的第 ( K ) 大的最小值。
  3. 检查函数:检查当前枚举的值是否满足条件。
  4. DFS 实现二分图匹配:通过深度优先搜索(DFS)实现二分图的最大匹配。

详细注释和讲解

import sys

# 输入获取
n, m, k = map(int, input().split())  # 读取 N, M, K
matrix = [list(map(int, input().split())) for _ in range(n)]  # 读取矩阵


# DFS 实现二分图匹配
def dfs(i, kth, match, vis):
    """
    为行号 i 找到一个匹配的列号 j。
    :param i: 当前行号
    :param kth: 当前枚举的第 K 大值
    :param match: 记录列号匹配的行号
    :param vis: 记录列号是否被访问过
    :return: 是否找到匹配
    """
    # 遍历每一个列号 j
    for j in range(m):
        # 如果列号 j 未被访问过,并且矩阵中 matrix[i][j] <= kth
        if not vis[j] and matrix[i][j] <= kth:
            vis[j] = True  # 标记列号 j 为已访问

            # 如果列号 j 未配对,或者已配对但可以找到其他行号重新配对
            if match[j] == -1 or dfs(match[j], kth, match, vis):
                # 则当前行号 i 和列号 j 可以配对
                match[j] = i
                return True

    # 如果找不到匹配的列号,返回 False
    return False


# 检查函数
def check(kth):
    """
    检查当前枚举的 kth 是否满足条件。
    :param kth: 当前枚举的第 K 大值
    :return: 是否满足条件
    """
    # 利用二分图最大匹配来求解,小于等于 kth 的元素个数(即二分图最大匹配)
    smallerCount = 0

    # 记录每个列号的匹配成功的行号
    # 初始时每个列号都处于未配对状态,此时将列号配对的行号赋值为 -1
    match = [-1] * m

    # 遍历行号,每个行号对互相心仪的列号发起配对请求
    for i in range(n):
        # 记录增广路访问过的列号
        vis = [False] * m

        # 如果当前行号 i 可以找到匹配的列号,则 smallerCount + 1
        if dfs(i, kth, match, vis):
            smallerCount += 1

    # 判断是否满足条件:小于等于 kth 的元素个数是否 >= N - K + 1
    return smallerCount >= n - k + 1


# 算法入口
def getResult():
    """
    主函数,通过二分枚举找到第 K 大的最小值。
    :return: 第 K 大的最小值
    """
    low = 1  # 二分枚举的左边界
    high = -sys.maxsize  # 二分枚举的右边界,初始为系统最小值

    # 找到矩阵中的最大值,作为二分枚举的右边界
    for i in range(n):
        for j in range(m):
            high = max(high, matrix[i][j])

    # 二分枚举第 K 大的最小取值
    while low <= high:
        # mid 是被枚举出来的 N 个数中的第 K 大的最小取值
        mid = (low + high) >> 1  # 取中间值,相当于 (low + high) // 2

        # 检查 mid 是否可以作为 N 个数中第 K 大的值
        if check(mid):
            # 如果满足条件,尝试更小的值
            high = mid - 1
        else:
            # 如果不满足条件,尝试更大的值
            low = mid + 1

    # 返回结果
    return low


# 算法调用
print(getResult())

代码逻辑详解

1. 输入处理
  • 使用 input() 读取输入。
  • 解析第一行得到 ( N )、( M )、( K )。
  • 读取矩阵,并存储在 matrix 中。
2. 二分枚举
  • 初始化二分枚举的左右边界:
    • low = 1:最小值从 1 开始。
    • high:矩阵中的最大值。
  • 通过二分法枚举可能的第 ( K ) 大的最小值 mid
  • 调用 check(mid) 检查当前 mid 是否满足条件。
3. 检查函数 check(kth)
  • 目标:检查是否存在至少 ( N - K + 1 ) 个小于等于 kth 的元素,并且这些元素不在同一行或同一列。
  • 实现方式:
    • 使用二分图最大匹配算法。
    • 遍历每一行,尝试匹配一个列号。
    • 如果匹配成功,则 smallerCount + 1
    • 最终判断 smallerCount 是否大于等于 ( N - K + 1 )。
4. DFS 实现二分图匹配
  • 目标:为当前行号 i 找到一个匹配的列号 j
  • 实现方式:
    • 遍历所有列号 j
    • 如果列号 j 未被访问过,并且矩阵中 matrix[i][j] <= kth,则尝试匹配。
    • 如果列号 j 未配对,或者已配对但可以找到其他行号重新配对,则匹配成功。
    • 返回匹配结果。
5. 输出结果
  • 当二分枚举结束时,low 即为满足条件的最小值。

关键点总结

  1. 二分枚举:通过二分法高效枚举可能的第 ( K ) 大的最小值。
  2. 二分图匹配:将问题转化为二分图的最大匹配问题,确保选出的数不在同一行或同一列。
  3. DFS 实现匹配:通过 DFS 实现二分图的匹配过程,确保匹配的列号满足条件。

五、C/C++算法源码:

以下是 C语言C++ 版本的代码,并附带详细的中文注释和讲解。


C语言代码

#include <stdio.h>
#include <limits.h>
#include <math.h>

#define MAX_SIZE 150

#define bool int
#define TRUE 1
#define FALSE 0

int n, m, k;
int matrix[MAX_SIZE][MAX_SIZE];

// DFS 实现二分图匹配
bool dfs(int i, int kth, int match[], int vis[]) {
    // 行号 i 发起了配对请求
    // 遍历每一个列号 j
    for (int j = 0; j < m; j++) {
        // 如果列号 j 未被访问过,并且矩阵中 matrix[i][j] <= kth
        if (!vis[j] && matrix[i][j] <= kth) {
            vis[j] = TRUE; // 标记列号 j 为已访问

            // 如果列号 j 未配对,或者已配对但可以找到其他行号重新配对
            if (match[j] == -1 || dfs(match[j], kth, match, vis)) {
                // 则当前行号 i 和列号 j 可以配对
                match[j] = i;
                return TRUE;
            }
        }
    }

    // 如果找不到匹配的列号,返回 FALSE
    return FALSE;
}

// 检查函数:判断当前枚举的 kth 是否满足条件
bool check(int kth) {
    // 利用二分图最大匹配来求解,小于等于 kth 的元素个数(即二分图最大匹配)
    int smallerCount = 0;

    // 记录每个列号的匹配成功的行号
    int match[m];
    // 初始时每个列号都处于未配对状态,此时将列号配对的行号赋值为 -1
    for (int i = 0; i < m; i++) match[i] = -1;

    // 遍历行号,每个行号对互相心仪的列号发起配对请求
    for (int i = 0; i < n; i++) {
        // 记录增广路访问过的列号
        int vis[MAX_SIZE] = {FALSE};

        // 如果当前行号 i 可以找到匹配的列号,则 smallerCount + 1
        if (dfs(i, kth, match, vis)) {
            smallerCount++;
        }
    }

    // 判断是否满足条件:小于等于 kth 的元素个数是否 >= N - K + 1
    return smallerCount >= (n - k + 1);
}

int main() {
    // 读取输入的 N, M, K
    scanf("%d %d %d", &n, &m, &k);

    int min = 1; // 二分枚举的左边界
    int max = INT_MIN; // 二分枚举的右边界,初始为整型最小值

    // 读取矩阵,并找到矩阵中的最大值
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            scanf("%d", &matrix[i][j]);
            max = (int) fmax(max, matrix[i][j]); // 更新最大值
        }
    }

    // 二分枚举第 K 大的最小取值
    while (min <= max) {
        // mid 是被枚举出来的 N 个数中的第 K 大的最小取值
        int mid = (min + max) >> 1; // 取中间值,相当于 (min + max) / 2

        // 检查 mid 是否可以作为 N 个数中第 K 大的值
        if (check(mid)) {
            // 如果满足条件,尝试更小的值
            max = mid - 1;
        } else {
            // 如果不满足条件,尝试更大的值
            min = mid + 1;
        }
    }

    // 输出结果
    printf("%d\n", min);

    return 0;
}

C++代码

#include <iostream>
#include <vector>
#include <climits>
#include <algorithm>

using namespace std;

const int MAX_SIZE = 150;

int n, m, k;
vector<vector<int>> matrix(MAX_SIZE, vector<int>(MAX_SIZE));

// DFS 实现二分图匹配
bool dfs(int i, int kth, vector<int>& match, vector<bool>& vis) {
    // 行号 i 发起了配对请求
    // 遍历每一个列号 j
    for (int j = 0; j < m; j++) {
        // 如果列号 j 未被访问过,并且矩阵中 matrix[i][j] <= kth
        if (!vis[j] && matrix[i][j] <= kth) {
            vis[j] = true; // 标记列号 j 为已访问

            // 如果列号 j 未配对,或者已配对但可以找到其他行号重新配对
            if (match[j] == -1 || dfs(match[j], kth, match, vis)) {
                // 则当前行号 i 和列号 j 可以配对
                match[j] = i;
                return true;
            }
        }
    }

    // 如果找不到匹配的列号,返回 false
    return false;
}

// 检查函数:判断当前枚举的 kth 是否满足条件
bool check(int kth) {
    // 利用二分图最大匹配来求解,小于等于 kth 的元素个数(即二分图最大匹配)
    int smallerCount = 0;

    // 记录每个列号的匹配成功的行号
    vector<int> match(m, -1); // 初始时每个列号都处于未配对状态

    // 遍历行号,每个行号对互相心仪的列号发起配对请求
    for (int i = 0; i < n; i++) {
        // 记录增广路访问过的列号
        vector<bool> vis(m, false);

        // 如果当前行号 i 可以找到匹配的列号,则 smallerCount + 1
        if (dfs(i, kth, match, vis)) {
            smallerCount++;
        }
    }

    // 判断是否满足条件:小于等于 kth 的元素个数是否 >= N - K + 1
    return smallerCount >= (n - k + 1);
}

int main() {
    // 读取输入的 N, M, K
    cin >> n >> m >> k;

    int minVal = 1; // 二分枚举的左边界
    int maxVal = INT_MIN; // 二分枚举的右边界,初始为整型最小值

    // 读取矩阵,并找到矩阵中的最大值
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> matrix[i][j];
            maxVal = max(maxVal, matrix[i][j]); // 更新最大值
        }
    }

    // 二分枚举第 K 大的最小取值
    while (minVal <= maxVal) {
        // mid 是被枚举出来的 N 个数中的第 K 大的最小取值
        int mid = (minVal + maxVal) >> 1; // 取中间值,相当于 (minVal + maxVal) / 2

        // 检查 mid 是否可以作为 N 个数中第 K 大的值
        if (check(mid)) {
            // 如果满足条件,尝试更小的值
            maxVal = mid - 1;
        } else {
            // 如果不满足条件,尝试更大的值
            minVal = mid + 1;
        }
    }

    // 输出结果
    cout << minVal << endl;

    return 0;
}

代码逻辑详解

1. 输入处理
  • 读取输入的 ( N )、( M )、( K )。
  • 读取矩阵,并找到矩阵中的最大值 maxVal,作为二分枚举的右边界。
2. 二分枚举
  • 初始化二分枚举的左右边界:
    • minVal = 1:最小值从 1 开始。
    • maxVal:矩阵中的最大值。
  • 通过二分法枚举可能的第 ( K ) 大的最小值 mid
  • 调用 check(mid) 检查当前 mid 是否满足条件。
3. 检查函数 check(kth)
  • 目标:检查是否存在至少 ( N - K + 1 ) 个小于等于 kth 的元素,并且这些元素不在同一行或同一列。
  • 实现方式:
    • 使用二分图最大匹配算法。
    • 遍历每一行,尝试匹配一个列号。
    • 如果匹配成功,则 smallerCount + 1
    • 最终判断 smallerCount 是否大于等于 ( N - K + 1 )。
4. DFS 实现二分图匹配
  • 目标:为当前行号 i 找到一个匹配的列号 j
  • 实现方式:
    • 遍历所有列号 j
    • 如果列号 j 未被访问过,并且矩阵中 matrix[i][j] <= kth,则尝试匹配。
    • 如果列号 j 未配对,或者已配对但可以找到其他行号重新配对,则匹配成功。
    • 返回匹配结果。
5. 输出结果
  • 当二分枚举结束时,minVal 即为满足条件的最小值。

关键点总结

  1. 二分枚举:通过二分法高效枚举可能的第 ( K ) 大的最小值。
  2. 二分图匹配:将问题转化为二分图的最大匹配问题,确保选出的数不在同一行或同一列。
  3. DFS 实现匹配:通过 DFS 实现二分图的匹配过程,确保匹配的列号满足条件。

通过以上注释和讲解,你应该能够理解代码的每一部分逻辑和实现方式。如果还有疑问,欢迎随时提出!

六、尾言

什么是华为OD?

华为OD(Outsourcing Developer,外包开发工程师)是华为针对软件开发工程师岗位的一种招聘形式,主要包括笔试、技术面试以及综合面试等环节。尤其在笔试部分,算法题的机试至关重要。

为什么刷题很重要?

  1. 机试是进入技术面的第一关:
    华为OD机试(常被称为机考)主要考察算法和编程能力。只有通过机试,才能进入后续的技术面试环节。

  2. 技术面试需要手撕代码:
    技术一面和二面通常会涉及现场编写代码或算法题。面试官会注重考察候选人的思路清晰度、代码规范性以及解决问题的能力。因此提前刷题、多练习是通过面试的重要保障。

  3. 入职后的可信考试:
    入职华为后,还需要通过“可信考试”。可信考试分为三个等级:

    • 入门级:主要考察基础算法与编程能力。
    • 工作级:更贴近实际业务需求,可能涉及复杂的算法或与工作内容相关的场景题目。
    • 专业级:最高等级,考察深层次的算法以及优化能力,与薪资直接挂钩。

刷题策略与说明:

2024年8月14日之后,华为OD机试的题库转为 E卷,由往年题库(D卷、A卷、B卷、C卷)和全新题目组成。刷题时可以参考以下策略:

  1. 关注历年真题:

    • 题库中的旧题占比较大,建议优先刷历年的A卷、B卷、C卷、D卷题目。
    • 对于每道题目,建议深度理解其解题思路、代码实现,以及相关算法的适用场景。
  2. 适应新题目:

    • E卷中包含全新题目,需要掌握全面的算法知识和一定的灵活应对能力。
    • 建议关注新的刷题平台或交流群,获取最新题目的解析和动态。
  3. 掌握常见算法:
    华为OD考试通常涉及以下算法和数据结构:

    • 排序算法(快速排序、归并排序等)
    • 动态规划(背包问题、最长公共子序列等)
    • 贪心算法
    • 栈、队列、链表的操作
    • 图论(最短路径、最小生成树等)
    • 滑动窗口、双指针算法
  4. 保持编程规范:

    • 注重代码的可读性和注释的清晰度。
    • 熟练使用常见编程语言,如C++、Java、Python等。

如何获取资源?

  1. 官方参考:

    • 华为招聘官网或相关的招聘平台会有一些参考信息。
    • 华为OD的相关公众号可能也会发布相关的刷题资料或学习资源。
  2. 加入刷题社区:

    • 找到可信的刷题交流群,与其他备考的小伙伴交流经验。
    • 关注知名的刷题网站,如LeetCode、牛客网等,这些平台上有许多华为OD的历年真题和解析。
  3. 寻找系统性的教程:

    • 学习一本经典的算法书籍,例如《算法导论》《剑指Offer》《编程之美》等。
    • 完成系统的学习课程,例如数据结构与算法的在线课程。

积极心态与持续努力:

刷题的过程可能会比较枯燥,但它能够显著提升编程能力和算法思维。无论是为了通过华为OD的招聘考试,还是为了未来的职业发展,这些积累都会成为重要的财富。

考试注意细节

  1. 本地编写代码

    • 在本地 IDE(如 VS Code、PyCharm 等)上编写、保存和调试代码,确保逻辑正确后再复制粘贴到考试页面。这样可以减少语法错误,提高代码准确性。
  2. 调整心态,保持冷静

    • 遇到提示不足或实现不确定的问题时,不必慌张,可以采用更简单或更有把握的方法替代,确保思路清晰。
  3. 输入输出完整性

    • 注意训练和考试时都需要编写完整的输入输出代码,尤其是和题目示例保持一致。完成代码后务必及时调试,确保功能符合要求。
  4. 快捷键使用

    • 删除行可用 Ctrl+D,复制、粘贴和撤销分别为 Ctrl+CCtrl+VCtrl+Z,这些可以正常使用。
    • 避免使用 Ctrl+S,以免触发浏览器的保存功能。
  5. 浏览器要求

    • 使用最新版的 Google Chrome 浏览器完成考试,确保摄像头开启并正常工作。考试期间不要切换到其他网站,以免影响考试成绩。
  6. 交卷相关

    • 答题前,务必仔细查看题目示例,避免遗漏要求。
    • 每完成一道题后,点击【保存并调试】按钮,多次保存和调试是允许的,系统会记录得分最高的一次结果。完成所有题目后,点击【提交本题型】按钮。
    • 确保在考试结束前提交试卷,避免因未保存或调试失误而丢分。
  7. 时间和分数安排

    • 总时间:150 分钟;总分:400 分。
    • 试卷结构:2 道一星难度题(每题 100 分),1 道二星难度题(200 分)。及格分为 150 分。合理分配时间,优先完成自己擅长的题目。
  8. 考试环境准备

    • 考试前请备好草稿纸和笔。考试中尽量避免离开座位,确保监控画面正常。
    • 如需上厕所,请提前规划好时间以减少中途离开监控的可能性。
  9. 技术问题处理

    • 如果考试中遇到断电、断网、死机等技术问题,可以关闭浏览器并重新打开试卷链接继续作答。
    • 出现其他问题,请第一时间联系 HR 或监考人员进行反馈。

祝你考试顺利,取得理想成绩!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2284903.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI 浪潮席卷中国年,开启科技新春新纪元

在这博主提前祝大家蛇年快乐呀&#xff01;&#xff01;&#xff01; 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;其影响力已经渗透到社会生活的方方面面。在中国传统节日 —— 春节期间&#xff0c;AI 技术也展现出了巨大的潜力&#xff0c;为中国年带…

Python 数据分析 - Matplotlib 绘图

Python 数据分析 - Matplotlib 绘图 简介绘图折线图单线多线子图 散点图直方图条形图纵置横置多条 饼图 简介 Matplotlib 是 Python 提供的一个绘图库&#xff0c;通过该库我们可以很容易的绘制出折线图、直方图、散点图、饼图等丰富的统计图&#xff0c;安装使用 pip install…

搭建Spark分布式集群

1&#xff0c;下载 下载 spark-3.5.4-bin-without-hadoop.tgz 地址&#xff1a; https://downloads.apache.org/spark/spark-3.5.4/ 2&#xff0c;安装 通过虚拟机设置共享文件夹将需要的安装包复制到linux虚拟机中 localhost1。虚拟机的共享盘在 /mnt/hgfs/。 将共享盘安装…

新年祝词(原创)

新年将至&#xff0c;福进万户。 家家团圆&#xff0c;事事顺心。 喜迎财神&#xff0c;多寿添金。 瑞兽迎春&#xff0c;炮竹声起。 趋吉避凶&#xff0c;蛇年大吉。 中华崛起&#xff0c;人人自强。 天下大同&#xff0c;百姓富足。 有情有义&#xff0c;平易近人。 …

线上突发:MySQL 自增 ID 用完,怎么办?

线上突发&#xff1a;MySQL 自增 ID 用完&#xff0c;怎么办&#xff1f; 1. 问题背景2. 场景复现3. 自增id用完怎么办&#xff1f;4. 总结 1. 问题背景 最近&#xff0c;我们在数据库巡检的时候发现了一个问题&#xff1a;线上的地址表自增主键用的是int类型。随着业务越做越…

ESP32 I2S音频总线学习笔记(二):I2S读取INMP441音频数据

简介 在这个系列的上一篇文章中&#xff0c;我们介绍了ESP32 I2S音频总线的相关知识&#xff0c;简要了解了什么是I2S总线、它的通信格式&#xff0c;以及相关的底层API函数。没有看过上篇文章的可以点击文章进行回顾&#xff1a; ESP32 I2S音频总线学习笔记&#xff08;一&a…

一文简单回顾Java中的String、StringBuilder、StringBuffer

简单说下String、StringBuilder、StringBuffer的区别 String、StringBuffer、StringBuilder在Java中都是用于处理字符串的&#xff0c;它们之间的区别是String是不可变的&#xff0c;平常开发用的最多&#xff0c;当遇到大量字符串连接的时候&#xff0c;就用StringBuilder&am…

matlab中,fill命令用法

在 MATLAB 中&#xff0c;fill 命令用于创建填充多边形的图形对象。使用 fill 可以在二维坐标系中绘制填充的区域&#xff0c;通常用于绘制图形的背景或显示数据分布。 基本语法 fill(X, Y, C)X 和 Y 是同样长度的向量&#xff0c;定义了多边形的顶点坐标。C 是颜色&#xff0…

计算机网络之链路层

本文章目录结构出自于《王道计算机考研 计算机网络_哔哩哔哩_bilibili》 02 数据链路层 在网上看到其他人做了详细的笔记&#xff0c;就不再多余写了&#xff0c;直接参考着学习吧。 1 详解数据链路层-数据链路层的功能【王道计算机网络笔记】_wx63088f6683f8f的技术博客_51C…

lib.exe正确用法winhv.lib生成方法

lib.exe /def:winhv.def /OUT:winhv.lib /machine:x64 winhv.def注意是 winhv.sys要不然会变成dll LIBRARY winhv.sys EXPORTSWinHvAllocateOverlayPagesWinHvDisablePartitionVtlWinHvDisableVpVtlWinHvEnablePartitionVtlWinHvEnableVpVtlWinHvFreeOverlayPagesWinHvGetCurr…

react-bn-面试

1.主要内容 工作台待办 实现思路&#xff1a; 1&#xff0c;待办list由后端返回&#xff0c;固定需要的字段有id(查详细)、type(本条待办的类型)&#xff0c;还可能需要时间&#xff0c;状态等 2&#xff0c;一个集中处理待办中转路由页&#xff0c;所有待办都跳转到这个页面…

Linux:一切皆文件

**文件描述符**&#xff1a;它是一种特殊的索引&#xff0c;本质上是进程中file_struct结构体成员fd_array数组的下标。在Linux等系统中&#xff0c;文件描述符是一个非负整数&#xff0c;用于标识打开的文件&#xff0c;是内核为了高效管理已被打开的文件所创建的索引。通过文…

【物联网】ARM核常用指令(详解):数据传送、计算、位运算、比较、跳转、内存访问、CPSR/SPSR、流水线及伪指令

文章目录 指令格式&#xff08;重点&#xff09;1. 立即数2. 寄存器位移 一、数据传送指令1. MOV指令2. MVN指令3. LDR指令 二、数据计算指令1. ADD指令1. SUB指令1. MUL指令 三、位运算指令1. AND指令2. ORR指令3. EOR指令4. BIC指令 四、比较指令五、跳转指令1. B/BL指令2. l…

项目集成Nacos

文章目录 1.环境搭建1.创建模块 sunrays-common-cloud-nacos-starter2.目录结构3.pom.xml4.自动配置1.NacosAutoConfiguration.java2.spring.factories 5.引入cloud模块通用依赖 2.测试1.创建模块 sunrays-common-cloud-nacos-starter-demo2.目录结构3.pom.xml4.application.ym…

QT交叉编译环境搭建(Cmake和qmake)

介绍一共有两种方法&#xff08;基于qmake和cmake&#xff09;&#xff1a; 1.直接调用虚拟机中的交叉编译工具编译 2.在QT中新建编译套件kits camke和qmake的区别&#xff1a;CMake 和 qmake 都是自动化构建工具&#xff0c;用于简化构建过程&#xff0c;管理编译设置&…

[cg] 使用snapgragon 对UE5.3抓帧

最近想要抓opengl 的api&#xff0c;renderdoc在起应用时会闪退&#xff08;具体原因还不知道&#xff09;&#xff0c;试了下snapgraon, 还是可以的 官网需要注册登录后下载&#xff0c;官网路径&#xff1a;Developer | Qualcomm 为了方便贴上已经下载好的exe安装包&#x…

物业巡更系统在现代社区管理中的优势与应用探讨

内容概要 在现代社区管理中&#xff0c;物业巡更系统正逐渐成为一种不可或缺的工具。结合先进的智能技术&#xff0c;这些系统能够有效地提升社区管理的各个方面&#xff0c;尤其是在巡检效率和信息透明度方面。通过实时记录巡检数据&#xff0c;物业管理人员能够确保工作人员…

速通Docker === Docker Compose

目录 Docker Compose 简介 Docker Compose 常用命令 使用 Docker Compose 启动 WordPress 普通启动方式&#xff08;使用 Docker 命令&#xff09; 使用 Docker Compose 启动 Docker Compose 的特性 Docker Compose 简介 Docker Compose 是一个用于定义和运行多容器 Dock…

Spring MVC 综合案例

目录 一. 加法计算器 1. 准备工作 2. 约定前后端交互接口 需求分析 接口定义 3. 服务器端代码 4. 运行测试 二. 用户登录 1. 准备工作 2. 约定前后端交互接口 需求分析 接口定义 (1) 登录界面接口 (2) 首页接口 3. 服务器端代码 4. 运行测试 三. 留言板 1. 准备…

数据分析系列--③RapidMiner算子说明及数据预处理

一、算子说明 1 新建过程 2 算子状态灯 状态灯说明: (1)状态指示灯&#xff1a; 红色:指示灯说明有参数未被设置或输入端口未被连接等问题; 黄色:指示灯说明还未执行算子&#xff0c;不管配置是否基本齐全; 绿色:指示灯说明一切正常&#xff0c;已成功执行算子。 (2)三角…