【2024年华为OD机试】(A卷,100分)- 幻方修复 (JavaScriptJava PythonC/C++)

ops/2025/1/23 3:27:11/

在这里插入图片描述

一、问题描述

幻方修复问题

题目描述

幻方(Magic Square)是一个由 1~N²,共 N² 个整数构成的 N×N 矩阵,满足每行、列和对角线上的数字和相等。

上回你已经帮助小明将写错一个数字的幻方进行了修复,小明在感谢之余也想进一步试试你的水平,于是他准备了有两个数字发生了位置交换的幻方。

你可以把这两个交换的数字找出来并且改正吗?


输入描述

  • 第一行输入一个整数 N,代表带校验幻方的阶数(3 ≤ N < 50)。
  • 接下来的 N 行,每行 N 个整数,空格隔开(1 ≤ 每个整数 ≤ N²)。

输出描述

  • 输出两行,代表两条纠正信息。
  • 每行包含三个整数,分别是:出错行号、出错列号、应填入的数字(末尾无空格)。
  • 注意:先输出行号小的,若行号相同则先输出列号小的。

用例

示例输入

3
8 1 9
3 5 7
4 6 2

示例输出

1 3 6
3 2 9

题目解析

解决该问题需要结合幻方的特性以及一些数学规律,以下详细介绍解题思路。

幻方特性

幻方中每一条直线上的数的和叫作 幻和。公式如下:

幻和 = 所有数的和 ÷ 阶数

  • 每行、列以及两条对角线上的数字和均为幻和。
  • 如果将幻方中两个数字交换,其行、列和对角线的和将不再符合幻和。

解题思路

1. 计算幻和

通过公式计算幻和:

幻和 = (N² × (N² + 1) / 2) ÷ N

  • N² × (N² + 1) / 2 是 1~N² 的总和。
  • 将总和除以阶数 N,得到幻和。

2. 找出行和与列和不符合幻和的行和列

遍历每行和每列,计算其和,记录所有不满足幻和的行和列。

  • 如果某一行的和 ≠ 幻和,则该行可能存在错误。
  • 如果某一列的和 ≠ 幻和,则该列可能存在错误。

3. 区分错误类型

幻方可能出现以下三种错误情况:

  1. 两个交换的数字在不同的行和列中

    • 即两行和两列的和都与幻和不符。
    • 错误的两行和两列的交点是可能的错误位置。
  2. 两个交换的数字在同一行中

    • 即所有行的和均符合幻和,但有两个列的和不符。
    • 错误的两列中的所有行交点是可能的错误位置。
  3. 两个交换的数字在同一列中

    • 即所有列的和均符合幻和,但有两行的和不符。
    • 错误的两行中的所有列交点是可能的错误位置。

4. 尝试复原幻方

对于每种错误类型,在可能的错误位置中进行两点组合,尝试交换两点的数值。

  • 如果交换后满足幻方的所有行、列和对角线的和均为幻和,则找到了正确的两点。

5. 特殊情况
  1. 两个交换的数字同时出现在对角线上

    • 特别是主对角线或副对角线。
    • 需要验证对角线和是否符合幻和。
  2. 边界问题

    • 确保行号和列号均从 1 开始,而非从 0 开始。

示例分析

示例输入

  • N = 3
  • 输入矩阵:
8 1 9
3 5 7
4 6 2

解题步骤

  1. 计算幻和
    ( 幻和 = \frac{(3^2 \times (3^2 + 1) / 2)}{3} = 15 )

  2. 找出不符合幻和的行和列

    • 第 1 行和 = (8 + 1 + 9 = 18)(错误)。
    • 第 3 行和 = (4 + 6 + 2 = 12)(错误)。
    • 第 2 列和 = (1 + 5 + 6 = 12)(错误)。
    • 第 3 列和 = (9 + 7 + 2 = 18)(错误)。
  3. 确定可能的错误点

    • 错误点可能在:
      • 第 1 行第 3 列(数值为 9)
      • 第 3 行第 2 列(数值为 6)
  4. 交换验证

    • 交换 (9) 和 (6) 后:
      • 新矩阵为:

        8 1 6
        3 5 7
        4 9 2
        
      • 每行和、每列和以及对角线和均为 15,满足幻方要求。

  5. 输出结果

    • 错误点 1:第 1 行第 3 列,改为 6。
    • 错误点 2:第 3 行第 2 列,改为 9。

示例输出

1 3 6
3 2 9

不同阶数幻方示例

四阶幻方

输入矩阵:

4
1 8 11 14
12 13 2 7
6 3 16 9
15 10 5 4

五阶幻方

输入矩阵:

5
2 23 16 10 14
20 9 12 3 21
13 1 25 19 7
24 17 8 11 5
6 15 4 22 18

六阶幻方

输入矩阵:

6
21 23 4 1 32 30
22 24 2 3 31 29
28 27 17 18 9 12
25 26 19 20 11 10
8 6 34 36 13 14
7 5 35 33 15 16

七阶幻方

输入矩阵:

7
46 15 40 9 34 3 28
21 39 8 33 2 27 45
38 14 32 1 26 44 20
13 31 7 25 43 19 37
30 6 24 49 18 36 12
5 23 48 17 42 11 29
22 47 16 41 10 35 4

八阶幻方

输入矩阵:

8
64 2 62 4 5 59 7 57
9 55 11 53 52 14 50 16
48 18 46 20 21 43 23 41
25 39 27 37 36 30 34 32
33 31 35 29 28 38 26 40
24 42 22 44 45 19 47 17
49 15 51 13 12 54 10 56
8 58 6 60 61 3 63 1

九阶幻方

输入矩阵:

9
47 58 69 80 1 12 23 34 45
57 68 79 9 11 22 33 44 46
67 78 8 10 21 32 43 54 56
77 7 18 20 31 42 53 55 66
6 17 19 30 41 52 63 65 76
16 27 29 40 51 62 64 75 5
26 28 39 50 61 72 74 4 15
36 38 49 60 71 73 3 14 25
37 48 59 70 81 2 13 24 35

十阶幻方

输入矩阵:

10
92 99 1 8 15 67 74 51 58 40
98 80 7 14 16 73 55 57 64 41
4 81 88 20 22 54 56 63 70 47
85 87 19 21 3 60 62 69 71 28
86 93 25 2 9 61 68 75 52 34
17 24 76 83 90 42 49 26 33 65
23 5 82 89 91 48 30 32 39 66
79 6 13 95 97 29 31 38 45 72
10 12 94 96 78 35 37 44 46 53
11 18 100 77 84 36 43 50 27 59

二、JavaScript算法源码

以下是对代码的详细注释和讲解,力求让每一步逻辑都清晰且容易理解。


代码分解与详细讲解

1. 基础输入与初始化

javascript>javascript">const readline = require("readline");const rl = readline.createInterface({input: process.stdin,output: process.stdout,
});const lines = [];
let n, matrix, magic_sum;
  • 使用 readline 模块来读取控制台输入。
  • 定义 lines 数组存储每一行输入。
  • 定义 n 表示幻方的阶数,matrix 表示整个幻方矩阵,magic_sum 表示幻和。

2. 输入数据处理

javascript>javascript">rl.on("line", (line) => {lines.push(line);if (lines.length == 1) {n = lines[0] - 0; // 第一个输入是幻方的阶数}if (n && lines.length == n + 1) {lines.shift(); // 去掉第一行阶数matrix = lines.map((line) => line.split(" ").map(Number)); // 将剩下的每一行转为数字矩阵getResult(); // 调用主函数进行幻方修复lines.length = 0; // 处理完成后清空输入}
});
  • 监听每一行输入,将其存储到 lines 中。
  • lines 的长度等于 n + 1(阶数行 + 矩阵行数),触发处理逻辑:
    1. 移除第一行阶数信息。
    2. 将输入的矩阵转换为二维数字数组。
    3. 调用 getResult() 函数开始解题逻辑。

3. 主逻辑函数:getResult

3.1 计算幻和
javascript>javascript">function getResult() {magic_sum = 0; // 初始化幻和for (let i = 0; i < n; i++) {for (let j = 0; j < n; j++) magic_sum += matrix[i][j]; // 累加整个矩阵的所有数字}magic_sum /= n; // 幻和 = 总和 ÷ 阶数
}
  • 遍历整个矩阵,计算所有数字的总和。
  • 将总和除以阶数 n,得到幻和 magic_sum

3.2 记录不正确的行和列
javascript>javascript">  const rows = [];const cols = [];for (let row = 0; row < n; row++) {if (getRowSum(row) != magic_sum) rows.push(row); // 如果行和不等于幻和,记录该行号}for (let col = 0; col < n; col++) {if (getColSum(col) != magic_sum) cols.push(col); // 如果列和不等于幻和,记录该列号}
  • 遍历每一行和每一列:
    • 如果某一行的和不等于幻和,记录其行号到 rows 数组。
    • 如果某一列的和不等于幻和,记录其列号到 cols 数组。

3.3 特殊情况处理
javascript>javascript">  if (rows.length == 0) {for (let i = 0; i < n; i++) rows.push(i); // 如果所有行都正确,则可能是在同一行中交换,记录所有行号}if (cols.length == 0) {for (let i = 0; i < n; i++) cols.push(i); // 如果所有列都正确,则可能是在同一列中交换,记录所有列号}
  • 如果没有发现错误的行,说明交换的两个数字位于同一行,可能出现在任何行中。
  • 如果没有发现错误的列,说明交换的两个数字位于同一列,可能出现在任何列中。

3.4 生成可能的错误点坐标
javascript>javascript">  const positions = [];for (let row of rows) {for (let col of cols) positions.push([row, col]); // 行和列的笛卡尔积,生成所有可能的错误点}
  • 将所有可能出错的行和列进行笛卡尔积,生成可能的错误点坐标。

3.5 遍历可能的错误点组合,尝试修复
javascript>javascript">  for (let i = 0; i < positions.length; i++) {for (let j = i + 1; j < positions.length; j++) {if (isValid(positions[i], positions[j])) return; // 如果找到正确的交换位置,直接返回}}
}
  • 组合错误点的所有可能两点组合。
  • 对每一对点调用 isValid 函数,尝试交换并验证幻方是否正确。

4. 验证函数:isValid

4.1 获取交换点并交换值
javascript>javascript">function isValid(pos1, pos2) {const [x1, y1] = pos1;const [x2, y2] = pos2;let tmp = matrix[x1][y1];matrix[x1][y1] = matrix[x2][y2];matrix[x2][y2] = tmp;
  • 获取两个可能出错点的坐标。
  • 交换这两个点的值。

4.2 验证幻方是否正确
javascript>javascript">  let ans = getDiagonalSum1() == magic_sum && getDiagonalSum2() == magic_sum;if (ans) {for (let r = 0; r < n; r++) {if (getRowSum(r) != magic_sum) {ans = false;break;}}}if (ans) {for (let c = 0; c < n; c++) {if (getColSum(c) != magic_sum) {ans = false;break;}}}
  • 验证主对角线和副对角线是否等于幻和。
  • 验证所有行和是否等于幻和。
  • 验证所有列和是否等于幻和。

4.3 输出结果或复原
javascript>javascript">  if (ans) {if (x1 * n + y1 < x2 * n + y2) {console.log(`${x1 + 1} ${y1 + 1} ${matrix[x1][y1]}`);console.log(`${x2 + 1} ${y2 + 1} ${matrix[x2][y2]}`);} else {console.log(`${x2 + 1} ${y2 + 1} ${matrix[x2][y2]}`);console.log(`${x1 + 1} ${y1 + 1} ${matrix[x1][y1]}`);}} else {tmp = matrix[x1][y1];matrix[x1][y1] = matrix[x2][y2];matrix[x2][y2] = tmp;}return ans;
}
  • 如果验证通过,输出修复后的坐标和对应值。
  • 如果验证失败,复原两点的值,继续尝试其他组合。

5. 辅助函数

5.1 行和计算
javascript>javascript">function getRowSum(r) {let sum = 0;for (let c = 0; c < n; c++) {sum += matrix[r][c];}return sum;
}
  • 累加某一行的所有元素,返回行和。

5.2 列和计算
javascript>javascript">function getColSum(c) {let sum = 0;for (let r = 0; r < n; r++) {sum += matrix[r][c];}return sum;
}
  • 累加某一列的所有元素,返回列和。

5.3 对角线和计算
javascript>javascript">function getDiagonalSum1() {let sum = 0;for (let i = 0; i < n; i++) {sum += matrix[i][i];}return sum;
}function getDiagonalSum2() {let sum = 0;for (let i = 0; i < n; i++) {sum += matrix[i][n - 1 - i];}return sum;
}
  • 分别计算主对角线(左上到右下)和副对角线(右上到左下)的和。

总结

  1. 核心逻辑

    • 找到错误的行和列。
    • 生成错误点的坐标。
    • 尝试交换并验证幻方的正确性。
  2. 特点

    • 代码逻辑清晰,逐步推进。
    • 通过幻方特性减少搜索空间。
  3. 注意事项

    • 坐标从 1 开始输出(而非从 0)。
    • 确保复原交换值,避免影响后续验证。

三、Java算法源码


代码讲解


1. 主函数结构

java">public static void main(String[] args) {Scanner sc = new Scanner(System.in);n = sc.nextInt(); // 读取幻方的阶数matrix = new int[n][n]; // 初始化幻方矩阵for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {matrix[i][j] = sc.nextInt(); // 读取矩阵中的数字magic_sum += matrix[i][j]; // 累加所有数字,计算总和}}magic_sum /= n; // 幻和 = 矩阵总和 ÷ 阶数getResult(); // 开始进行错误修复
}
  1. 输入处理:

    • 使用 Scanner 读取控制台输入。
    • n 表示幻方的阶数,matrix 是存储幻方的二维数组。
  2. 幻和计算:

    • 幻和(magic_sum)是矩阵所有数字的总和除以阶数 ( n )。
    • 这是幻方的核心特性,即每行、每列和每条对角线的和都等于幻和。
  3. 调用修复逻辑:

    • 输入处理完成后,调用 getResult() 函数,开始检查并修复幻方。

2. 获取修复结果

2.1 寻找不正确的行和列
java">public static void getResult() {// 记录 和不正确的行ArrayList<Integer> rows = new ArrayList<>();// 记录 和不正确的列ArrayList<Integer> cols = new ArrayList<>();// 检查每行的和是否等于幻和for (int row = 0; row < n; row++) {if (getRowSum(row) != magic_sum) {rows.add(row); // 记录不正确的行号}}// 检查每列的和是否等于幻和for (int col = 0; col < n; col++) {if (getColSum(col) != magic_sum) {cols.add(col); // 记录不正确的列号}}
  1. 记录错误行和列:
    • 遍历每一行和每一列,检查其总和是否等于幻和。
    • 如果某行的和不等于幻和,将该行号存入 rows
    • 如果某列的和不等于幻和,将该列号存入 cols

2.2 特殊情况处理
java">    // 两点处于同一行,不同列if (rows.size() == 0) {// 如果所有行都正确,则两点的行坐标可能是任意一行for (int i = 0; i < n; i++) rows.add(i);}// 两点处于同一列,不同行if (cols.size() == 0) {// 如果所有列都正确,则两点的列坐标可能是任意一列for (int i = 0; i < n; i++) cols.add(i);}
  1. 行或列完全正确的特殊情况:
    • 如果 rows 为空,则说明所有行的和均正确,交换的两个数字可能在同一行。
    • 如果 cols 为空,则说明所有列的和均正确,交换的两个数字可能在同一列。
    • 在这两种情况下,所有行或列都需要加入可能的错误位置。

2.3 生成可能的错误坐标
java">    // 行号 x 列号,就可以组合出坐标ArrayList<int[]> positions = new ArrayList<>();for (Integer row : rows) {for (Integer col : cols) {positions.add(new int[] {row, col});}}
  • 生成坐标:
    • 将所有错误行和列的组合生成坐标点。
    • 这些坐标点是可能发生错误的数字位置。

2.4 遍历尝试交换
java">    // 组合两点,尝试交换for (int i = 0; i < positions.size(); i++) {for (int j = i + 1; j < positions.size(); j++) {if (isValid(positions.get(i), positions.get(j))) return;}}
}
  • 两点组合:
    • 遍历所有可能的坐标点组合。
    • 对每一对点调用 isValid(),尝试交换并验证幻方是否符合要求。

3. 验证是否符合幻方

3.1 获取并交换值
java">public static boolean isValid(int[] pos1, int[] pos2) {int x1 = pos1[0], y1 = pos1[1];int x2 = pos2[0], y2 = pos2[1];// 交换两个点的值int tmp = matrix[x1][y1];matrix[x1][y1] = matrix[x2][y2];matrix[x2][y2] = tmp;
  • 交换两点的值:
    • 交换两个可能出错点的值,进行验证。

3.2 验证行、列和对角线
java">    boolean ans = magic_sum == getDiagonalSum1() && magic_sum == getDiagonalSum2();if (ans)for (int r = 0; r < n; r++) {if (getRowSum(r) != magic_sum) {ans = false;break;}}if (ans)for (int c = 0; c < n; c++) {if (getColSum(c) != magic_sum) {ans = false;break;}}
  1. 验证对角线:

    • 检查主对角线和副对角线是否等于幻和。
  2. 验证行和列:

    • 遍历所有行和列,检查其和是否等于幻和。

3.3 输出或复原
java">    if (ans) {if (x1 * n + y1 < x2 * n + y2) {System.out.println((x1 + 1) + " " + (y1 + 1) + " " + matrix[x1][y1]);System.out.println((x2 + 1) + " " + (y2 + 1) + " " + matrix[x2][y2]);} else {System.out.println((x2 + 1) + " " + (y2 + 1) + " " + matrix[x2][y2]);System.out.println((x1 + 1) + " " + (y1 + 1) + " " + matrix[x1][y1]);}} else {// 否则复原交换位置tmp = matrix[x1][y1];matrix[x1][y1] = matrix[x2][y2];matrix[x2][y2] = tmp;}return ans;
}
  1. 成功验证:

    • 如果交换后验证通过,输出两个点的坐标和修复后的值。
    • 按照坐标的字典序(行号优先,其次列号)输出。
  2. 验证失败:

    • 如果验证失败,复原交换的值。

4. 辅助函数

行和计算
java">public static int getRowSum(int r) {int sum = 0;for (int c = 0; c < n; c++) sum += matrix[r][c];return sum;
}
  • 计算第 r 行的所有数字之和。

列和计算
java">public static int getColSum(int c) {int sum = 0;for (int r = 0; r < n; r++) sum += matrix[r][c];return sum;
}
  • 计算第 c 列的所有数字之和。

对角线和计算
java">public static int getDiagonalSum1() {int sum = 0;for (int i = 0; i < n; i++) {sum += matrix[i][i];}return sum;
}
  • 计算主对角线(左上到右下)的和。
java">public static int getDiagonalSum2() {int sum = 0;for (int i = 0; i < n; i++) {sum += matrix[i][n - 1 - i];}return sum;
}
  • 计算副对角线(右上到左下)的和。

总结

  1. 整体逻辑:

    • 通过幻方特性(每行、列、对角线的和相等),逐步定位并修复问题。
  2. 关键优化:

    • 利用错误行列生成可能的错误点,大幅减少搜索空间。
  3. 注意事项:

    • 输出时行号和列号从 1 开始(人类习惯),而非从 0 开始(编程习惯)。
    • 确保在验证失败时复原交换值,避免影响后续尝试。

四、Python算法源码


代码讲解


1. 输入处理与初始化

python">n = int(input())  # 读取幻方的阶数
matrix = [list(map(int, input().split())) for _ in range(n)]  # 构建幻方矩阵
  • 输入处理:
    • 使用 input() 读取阶数 n
    • 使用列表解析读取 n 行的矩阵数据,并将每一行转换为整数列表。

2. 辅助函数

2.1 对角线和计算
python">def getDiagonalSum1():total = 0for i in range(n):total += matrix[i][i]  # 左上到右下return totaldef getDiagonalSum2():total = 0for i in range(n):total += matrix[i][n - 1 - i]  # 右上到左下return total
  • 主对角线和(左上到右下)副对角线和(右上到左下) 的计算。
2.2 行和列和计算
python">def getRowSum(r):return sum(matrix[r])  # 计算第 r 行的和def getColSum(c):total = 0for r in range(n):total += matrix[r][c]  # 计算第 c 列的和return total
  • 行和:直接使用 sum 函数计算某一行的总和。
  • 列和:累加某一列各元素的值,得出总和。
2.3 坐标解析
python">def getPos(pos):x = pos // ny = pos % nreturn [x, y]
  • 将一维索引转换为二维坐标。虽然此函数在当前代码中未被使用,但在某些情况下可以用于根据单个索引计算行列坐标。

3. 验证与交换逻辑

python">def isValid(pos1, pos2, magic_sum):x1, y1 = pos1x2, y2 = pos2# 交换两个点的值matrix[x1][y1], matrix[x2][y2] = matrix[x2][y2], matrix[x1][y1]# 验证所有行、列、对角线的和是否为幻和ans = magic_sum == getDiagonalSum1() and magic_sum == getDiagonalSum2()if ans:for r in range(n):if getRowSum(r) != magic_sum:ans = Falsebreakif ans:for c in range(n):if getColSum(c) != magic_sum:ans = Falsebreak# 如果正确,输出结果if ans:if pos1 < pos2:print(f"{x1 + 1} {y1 + 1} {matrix[x1][y1]}")print(f"{x2 + 1} {y2 + 1} {matrix[x2][y2]}")else:print(f"{x2 + 1} {y2 + 1} {matrix[x2][y2]}")print(f"{x1 + 1} {y1 + 1} {matrix[x1][y1]}")else:# 复原交换位置matrix[x1][y1], matrix[x2][y2] = matrix[x2][y2], matrix[x1][y1]return ans
  • 交换和验证
    • pos1pos2 位置上的值交换。
    • 验证整个矩阵的行、列和对角线是否满足幻和。
    • 如果满足,按顺序输出交换的位置和对应的数值。
    • 如果不满足,复原交换的值。

4. 主逻辑:修复幻方

python">def getResult():# 计算幻和magic_sum = sum(sum(row) for row in matrix) / n# 记录可能出错的行和列rows = [r for r in range(n) if getRowSum(r) != magic_sum]cols = [c for c in range(n) if getColSum(c) != magic_sum]# 特殊情况下,行或列全正确,假设交换在同一行或列if len(rows) == 0:rows = list(range(n))if len(cols) == 0:cols = list(range(n))# 生成所有可能错误的坐标点positions = [(r, c) for r in rows for c in cols]# 检查所有可能的两点组合for i in range(len(positions)):for j in range(i + 1, len(positions)):if isValid(positions[i], positions[j], magic_sum):return
  • 计算幻和:计算并确定幻和的值。
  • 记录异常行和列:将不等于幻和的行和列记录下来。
  • 特殊情况处理:如果所有行或列都正确,则假设交换发生在同一行或列的不同位置。
  • 生成可能错误的坐标:通过行和列的组合生成所有可能的错误坐标。
  • 验证与修复:通过所有可能的两点组合,调用 isValid 验证和修复幻方。

总结

  • 通过逐步分析幻方的特性,利用行、列、对角线的和判断错误位置。
  • 通过坐标组合及交换逻辑验证和修复幻方。
  • 输出结果时,注意格式化输出坐标(从 1 开始)及对应的数值。
  • 代码逻辑紧凑,利用 Python 的列表解析和函数功能使得代码清晰且简洁。

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

C++ 代码

#include <iostream>
#include <vector>using namespace std;// 声明全局变量
int n;
vector<vector<int>> matrix;// 求对角线的和(右上,到左下)
int getDiagonalSum2() {int total = 0;for (int i = 0; i < n; ++i) {total += matrix[i][n - 1 - i];}return total;
}// 求对角线的和(左上,到右下)
int getDiagonalSum1() {int total = 0;for (int i = 0; i < n; ++i) {total += matrix[i][i];}return total;
}// 求对应列的和
int getColSum(int c) {int total = 0;for (int r = 0; r < n; ++r) {total += matrix[r][c];}return total;
}// 求对应行的和
int getRowSum(int r) {int total = 0;for (int c = 0; c < n; ++c) {total += matrix[r][c];}return total;
}// 判断两个位置是否有效交换
bool isValid(pair<int, int> pos1, pair<int, int> pos2, int magic_sum) {int x1 = pos1.first, y1 = pos1.second;int x2 = pos2.first, y2 = pos2.second;// 交换两个点的值swap(matrix[x1][y1], matrix[x2][y2]);// 判断两个对角线和是否等于幻和bool ans = (magic_sum == getDiagonalSum1() && magic_sum == getDiagonalSum2());// 判断每一行的和是否相等if (ans) {for (int r = 0; r < n; ++r) {if (getRowSum(r) != magic_sum) {ans = false;break;}}}// 判断每一列的和是否相等if (ans) {for (int c = 0; c < n; ++c) {if (getColSum(c) != magic_sum) {ans = false;break;}}}if (ans) {if (x1 * n + y1 < x2 * n + y2) {cout << x1 + 1 << " " << y1 + 1 << " " << matrix[x1][y1] << endl;cout << x2 + 1 << " " << y2 + 1 << " " << matrix[x2][y2] << endl;} else {cout << x2 + 1 << " " << y2 + 1 << " " << matrix[x2][y2] << endl;cout << x1 + 1 << " " << y1 + 1 << " " << matrix[x1][y1] << endl;}} else {// 复原swap(matrix[x1][y1], matrix[x2][y2]);}return ans;
}// 主函数,进行幻方修复
void getResult() {// 计算幻和int magic_sum = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < n; ++j) {magic_sum += matrix[i][j];}}magic_sum /= n;// 记录 和不正确的行vector<int> rows;// 记录 和不正确的列vector<int> cols;// 检查每一行for (int row = 0; row < n; ++row) {if (getRowSum(row) != magic_sum) {rows.push_back(row);}}// 检查每一列for (int col = 0; col < n; ++col) {if (getColSum(col) != magic_sum) {cols.push_back(col);}}// 两点处于同一行,不同列if (rows.empty()) {for (int i = 0; i < n; ++i) rows.push_back(i);}// 两点处于同一列,不同行if (cols.empty()) {for (int i = 0; i < n; ++i) cols.push_back(i);}// 组合出所有可能的错误点坐标vector<pair<int, int>> positions;for (int r : rows) {for (int c : cols) {positions.emplace_back(r, c);}}// 尝试所有组合进行交换for (size_t i = 0; i < positions.size(); ++i) {for (size_t j = i + 1; j < positions.size(); ++j) {if (isValid(positions[i], positions[j], magic_sum)) {return;}}}
}int main() {// 输入获取cin >> n;matrix.resize(n, vector<int>(n));for (int i = 0; i < n; ++i) {for (int j = 0; j < n; ++j) {cin >> matrix[i][j];}}// 调用算法getResult();return 0;
}

C++ 代码讲解

  1. 输入与初始化

    • 读取矩阵阶数 n 和幻方矩阵 matrix
    • 使用 vector 动态数组来存储矩阵。
  2. 辅助函数

    • getDiagonalSum1getDiagonalSum2 用于计算主对角线与副对角线的和。
    • getRowSumgetColSum 计算指定行或列的和。
  3. 修复逻辑

    • isValid 函数检查两个给定坐标点是否可以通过交换恢复幻方结构。
    • getResult 函数确定幻方错误的位置,并通过组合可能错误坐标进行交换尝试。
  4. 输出

    • 输出修复后正确的交换点坐标和对应的数值。

C 语言代码

#include <stdio.h>
#include <stdbool.h>// 声明全局变量
int n;
int matrix[100][100];  // 假设 n 不会超过 100// 求对角线的和(右上,到左下)
int getDiagonalSum2() {int total = 0;for (int i = 0; i < n; ++i) {total += matrix[i][n - 1 - i];}return total;
}// 求对角线的和(左上,到右下)
int getDiagonalSum1() {int total = 0;for (int i = 0; i < n; ++i) {total += matrix[i][i];}return total;
}// 求对应列的和
int getColSum(int c) {int total = 0;for (int r = 0; r < n; ++r) {total += matrix[r][c];}return total;
}// 求对应行的和
int getRowSum(int r) {int total = 0;for (int c = 0; c < n; ++c) {total += matrix[r][c];}return total;
}// 判断两个位置是否有效交换
bool isValid(int x1, int y1, int x2, int y2, int magic_sum) {// 交换两个点的值int tmp = matrix[x1][y1];matrix[x1][y1] = matrix[x2][y2];matrix[x2][y2] = tmp;// 判断两个对角线和是否等于幻和bool ans = (magic_sum == getDiagonalSum1() && magic_sum == getDiagonalSum2());// 判断每一行的和是否相等if (ans) {for (int r = 0; r < n; ++r) {if (getRowSum(r) != magic_sum) {ans = false;break;}}}// 判断每一列的和是否相等if (ans) {for (int c = 0; c < n; ++c) {if (getColSum(c) != magic_sum) {ans = false;break;}}}if (ans) {if (x1 * n + y1 < x2 * n + y2) {printf("%d %d %d\n", x1 + 1, y1 + 1, matrix[x1][y1]);printf("%d %d %d\n", x2 + 1, y2 + 1, matrix[x2][y2]);} else {printf("%d %d %d\n", x2 + 1, y2 + 1, matrix[x2][y2]);printf("%d %d %d\n", x1 + 1, y1 + 1, matrix[x1][y1]);}} else {// 复原tmp = matrix[x1][y1];matrix[x1][y1] = matrix[x2][y2];matrix[x2][y2] = tmp;}return ans;
}// 主函数,进行幻方修复
void getResult() {// 计算幻和int magic_sum = 0;for (int i = 0; i < n; ++i) {for (int j = 0; j < n; ++j) {magic_sum += matrix[i][j];}}magic_sum /= n;// 记录 和不正确的行int rows[100], rowCount = 0;// 记录 和不正确的列int cols[100], colCount = 0;// 检查每一行for (int row = 0; row < n; ++row) {if (getRowSum(row) != magic_sum) {rows[rowCount++] = row;}}// 检查每一列for (int col = 0; col < n; ++col) {if (getColSum(col) != magic_sum) {cols[colCount++] = col;}}// 两点处于同一行,不同列if (rowCount == 0) {for (int i = 0; i < n; ++i) rows[rowCount++] = i;}// 两点处于同一列,不同行if (colCount == 0) {for (int i = 0; i < n; ++i) cols[colCount++] = i;}// 尝试所有组合进行交换for (int i = 0; i < rowCount; ++i) {for (int j = 0; j < colCount; ++j) {for (int k = i + 1; k < rowCount; ++k) {for (int l = j + 1; l < colCount; ++l) {if (isValid(rows[i], cols[j], rows[k], cols[l], magic_sum)) {return;}}}}}
}int main() {// 输入获取scanf("%d", &n);for (int i = 0; i < n; ++i) {for (int j = 0; j < n; ++j) {scanf("%d", &matrix[i][j]);}}// 调用算法getResult();return 0;
}

C 语言代码讲解

  1. 输入与初始化

    • 读取矩阵阶数 n 和幻方矩阵 matrix
    • 使用二维数组 matrix 存储幻方数据,假设最大为 100x100。
  2. 辅助函数

    • getDiagonalSum1getDiagonalSum2 用于计算主对角线与副对角线的和。
    • getRowSumgetColSum 计算指定行或列的和。
  3. 修复逻辑

    • isValid 函数检查两个给定坐标点是否可以通过交换恢复幻方结构。
    • getResult 函数确定幻方错误的位置,并通过组合可能错误坐标进行交换尝试。
  4. 输出

    • 输出修复后正确的交换点坐标和对应的数值。

这两个版本的代码使用了与 Python 代码相似的逻辑,通过 C++ 和 C 语言的特性实现了幻方修复算法。需要注意,在 C 语言中,我们需要手动管理数组的大小,而在 C++ 中使用了 vector 可以自动调整大小。

六、尾言

什么是华为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.ppmy.cn/ops/152359.html

相关文章

日志(elk stack)基础语法学习,零基础学习

ELK Stack 是一组开源的日志管理工具&#xff0c;包括 Elasticsearch、Logstash 和 Kibana。Elasticsearch 用于存储和搜索日志数据&#xff0c;Logstash 用于收集和处理日志数据&#xff0c;而 Kibana 提供了一个强大的可视化界面来分析和监控这些数据。以下是 ELK Stack 的基…

SpringBoot 整合 Avro 与 Kafka

优质博文&#xff1a;IT-BLOG-CN 【需求】&#xff1a;生产者发送数据至 kafka 序列化使用 Avro&#xff0c;消费者通过 Avro 进行反序列化&#xff0c;并将数据通过 MyBatisPlus 存入数据库。 一、环境介绍 【1】Apache Avro 1.8&#xff1b;【2】Spring Kafka 1.2&#xf…

【STM32-学习笔记-10-】BKP备份寄存器+时间戳

文章目录 BKP备份寄存器Ⅰ、BKP简介1. BKP的基本功能2. BKP的存储容量3. BKP的访问和操作4. BKP的应用场景5. BKP的控制寄存器 Ⅱ、BKP基本结构Ⅲ、BKP函数Ⅳ、BKP使用示例 时间戳一、Unix时间戳二、时间戳的转换&#xff08;time.h函数介绍&#xff09;Ⅰ、time()Ⅱ、mktime()…

MySQL日期时间函数详解

简介 本文主要讲解MySQL中的日期时间函数&#xff0c;包括&#xff1a;NOW、CURRENT_TIMESTAMP、CURDATE、CURRENT_DATE、CURTIME、CURRENT_TIME、STR_TO_DATE、DATE_FORMAT、TIME_FORMAT、DATE、TIME、YEAR、MONTH、DAY、HOUR、MINUTE、SECOND、QUARTER、YEARWEEK、WEEKDAY、…

Nginx 反向代理与负载均衡配置实践

一、引言 在当今互联网架构中&#xff0c;Nginx作为一款高性能的HTTP和反向代理服务器&#xff0c;广泛应用于各种场景&#xff0c;为众多网站和应用提供了强大的支持。它能够高效地处理大量并发请求&#xff0c;实现反向代理与负载均衡功能&#xff0c;显著提升系统的性能、可…

C#语言的学习路线

C#语言的学习路线 C#作为一种现代编程语言&#xff0c;凭借其简洁的语法、强大的功能和广泛的应用&#xff0c;得到了越来越多开发者的青睐。无论是开发桌面应用、Web应用、游戏&#xff0c;还是云服务&#xff0c;C#都有着广泛的应用场景。本文将为有志于学习C#的读者提供一条…

macOS如何进入 Application Support 目录(cd: string not in pwd: Application)

错误信息 cd: string not in pwd: Application 表示在当前目录下找不到名为 Application Support 的目录。可能的原因如下&#xff1a; 拼写错误或路径错误&#xff1a;确保你输入的目录名称正确。目录名称是区分大小写的&#xff0c;因此请确保使用正确的大小写。正确的目录名…

【Linux】Linux命令:free

目录 1、作用2、命令使用格式3、常用参数说明4、输出结果说明4.1 行字段说明4.2 列字段说明 5、示例5.1 以人类易读的方式显示内存使用情况5.2 显示内存总和行5.3 以2秒为间隔&#xff0c;持续输出内存使用情况5.4 以2秒为间隔&#xff0c;输出5次内存使用情况 1、作用 free命令…