华为OD机试 2024E卷题库疯狂收录中,刷题点这里
专栏导读
本专栏收录于《华为OD机试真题(Python/JS/C/C++)》。
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。
一、题目描述
有一种特殊的加密算法,明文为一段数字串,经过密码本查找转换,生成另一段密文数字串。
规则如下
- 明文为一段数字串由0-9组成
- 密码本为数字0-9组成的二维数组
- 需要按明文串的数字顺序在密码本里找到同样的数字串,密码本里的数字串是由相邻的单元格数字组成,上下和左右是相邻的,注意:对角线不相邻,同一个单元格的数字不能重复使用。
- 每一位明文对应密文即为密码本中找到的单元格所在的行和列序号(序号从0开始)组成的两个数字。如明文第位Data[i]对应密码本单元格为Book[x][y],则明文第i位对应的密文为XY,X和Y之间用空格隔开
如果有多条密文,返回字符序最小的密文。如果密码本无法匹配,返回"error"
二、输入描述
第一行输入1个正整数N,代表明文的长度(1<= N <= 200)
第二行输入N个明文数字组成的序列Data[i] (整数: 0<= Data[i]<= 9)
第三行1个正整数M,代表密文的长度接下来M行,每行M个数,代表密文矩阵
三、输出描述
输出字典序最小密文.如果无法匹配,输出"error
四、测试用例
测试用例1:
1、输入
2
0 3
3
0 0 2
1 3 4
6 6 4
2、输出
0 1 1 1
3、说明
目标是找到密码本中所有与明文匹配的路径,并输出字典序最小的路径。
- 从位置 (0, 0) 开始:
- 当前单元格值是 0,匹配明文的第一个数字 0。
- 接下来在相邻单元格中寻找 3,可选的相邻单元格有:
- (0, 1) -> 值是 0
- (1, 0) -> 值是 1
- 没有找到匹配的数字 3,继续搜索下一个起点。
- 从位置 (0, 1) 开始:
- 当前单元格值是 0,匹配明文的第一个数字 0。
- 接下来在相邻单元格中寻找 3,可选的相邻单元格有:
- (0, 0) -> 值是 0
- (0, 2) -> 值是 2
- (1, 1) -> 值是 3(匹配!)
- 路径为:(0, 1) -> (1, 1),对应的密文为 0 1 1 1。
- 从其他起点继续搜索(如(0, 2), (1, 0), 等),但这些都不能找到有效的路径。
在所有可能的路径中,唯一找到的路径是从 (0, 1) -> (1, 1),对应的密文为 0 1 1 1。
测试用例2:
1、输入
2
0 5
3
0 0 2
1 3 4
6 6 4
2、输出
error
3、说明
- 从位置 (0, 0) 开始:
- 当前单元格值是 0,匹配明文的第一个数字 0。
- 接下来在相邻单元格中寻找 5,可选的相邻单元格有:
- (0, 1) -> 值是 0
- (1, 0) -> 值是 1
- 没有找到匹配的数字 5,继续搜索下一个起点。
- 从位置 (0, 1) 开始:
- 当前单元格值是 0,匹配明文的第一个数字 0。
- 接下来在相邻单元格中寻找 5,可选的相邻单元格有:
- (0, 0) -> 值是 0
- (0, 2) -> 值是 2
- (1, 1) -> 值是 3
- 没有找到匹配的数字 5,继续搜索下一个起点。
- 从其他起点继续搜索(如(0, 2), (1, 0), 等),但这些都不能找到有效的路径。
在密码本中,数字 5 并不存在,因此没有找到任何匹配路径。
五、解题思路
1、深度优先搜索DFS
DFS (Depth-First Search) 是一种用于遍历或搜索树或图的算法。它从一个起点出发,尽可能深入探索每一个分支,直到不能再深入为止,然后回溯到上一个节点继续探索其他分支。
在本题中,DFS 用于在密码本矩阵中查找与明文数字序列匹配的路径。每找到一个符合条件的单元格,继续递归搜索其相邻单元格,直到找到完整的明文序列或搜索失败。
2、为什么采用深度优先搜索DFS?
采用深度优先搜索(DFS)算法有以下几个原因:
DFS 是一种遍历或搜索图或树数据结构的算法,特别适用于路径搜索问题。因为它会从一个起点出发,尽可能深入到某个分支的尽头,然后回溯,探索其他未访问的分支。在本题中,我们需要在二维矩阵中找到一条特定的路径,DFS 是一种合适的选择。
在本题中,密码本的大小是有限的(最大 200x200),这使得 DFS 在时间和空间复杂度上是可接受的。DFS 的时间复杂度是 O(V + E),其中 V 是节点数,E 是边数。对于矩阵搜索问题,节点数和边数都是有限的,因此 DFS 是有效的。
3、具体步骤:
- 读取输入:从标准输入中读取明文长度、明文数字序列、密码本矩阵的大小和内容。
- 初始化数据结构:准备用于深度优先搜索(DFS)的辅助数据结构,包括密码本矩阵、访问记录矩阵和用于构建路径的字符串生成器。
- 遍历起点:从密码本矩阵中的每一个可能的起点开始,尝试匹配明文数字序列。
- 深度优先搜索(DFS):递归搜索从当前起点出发的所有可能路径:
- 检查当前单元格是否与明文的当前数字匹配。
- 如果匹配,继续搜索其相邻单元格(上下左右四个方向)。
- 在每一步搜索中,记录路径并标记已访问单元格,避免重复使用。
- 如果找到一条完整的路径,与当前已找到的最优路径比较,更新最优路径。
- 输出结果:遍历完所有可能的起点后,输出字典序最小的路径,如果没有找到匹配路径,输出 “error”。
六、Python算法源码
def dfs(book, data, visited, x, y, index, M, path, N):
global minPath
# 如果已经匹配到明文的最后一个数字
if index == N - 1:
current_path = " ".join(map(str, path))
# 更新字典序最小的路径
if minPath is None or current_path < minPath:
minPath = current_path
return
# 定义方向数组,用于表示上下左右四个方向
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
# 遍历四个方向
for i in range(4):
new_x = x + dx[i]
new_y = y + dy[i]
# 检查新的位置是否在矩阵范围内,是否未访问过,且与明文的下一个数字匹配
if 0 <= new_x < M and 0 <= new_y < M and not visited[new_x][new_y] and book[new_x][new_y] == data[index + 1]:
visited[new_x][new_y] = True # 标记新的位置为访问过
path.append((new_x, new_y)) # 更新路径
# 继续深度优先搜索
dfs(book, data, visited, new_x, new_y, index + 1, M, path, N)
path.pop() # 回溯,移除最后添加的路径
visited[new_x][new_y] = False # 取消访问标记
def main():
global minPath
minPath = None
# 读取输入
N = int(input())
data = list(map(int, input().split()))
M = int(input())
book = [list(map(int, input().split())) for _ in range(M)]
# 尝试从每一个位置开始搜索
for i in range(M):
for j in range(M):
# 如果当前单元格的值与明文的第一个数字匹配
if book[i][j] == data[0]:
visited = [[False] * M for _ in range(M)] # 记录访问过的单元格
visited[i][j] = True # 标记当前单元格为访问过
# 从当前位置开始深度优先搜索
dfs(book, data, visited, i, j, 0, M, [(i, j)], N)
# 输出结果
print(minPath if minPath is not None else "error")
if __name__ == "__main__":
main()
七、JavaScript算法源码
let dx = [-1, 1, 0, 0];
let dy = [0, 0, -1, 1];
let minPath = null; // 存储字典序最小的路径
function dfs(book, data, visited, x, y, index, M, path, N) {
// 如果已经匹配到明文的最后一个数字
if (index === N - 1) {
let currentPath = path.join(" ");
// 更新字典序最小的路径
if (minPath === null || currentPath < minPath) {
minPath = currentPath;
}
return;
}
// 遍历四个方向
for (let i = 0; i < 4; i++) {
let newX = x + dx[i];
let newY = y + dy[i];
// 检查新的位置是否在矩阵范围内,是否未访问过,且与明文的下一个数字匹配
if (newX >= 0 && newX < M && newY >= 0 && newY < M && !visited[newX][newY] && book[newX][newY] === data[index + 1]) {
visited[newX][newY] = true; // 标记新的位置为访问过
path.push(newX + " " + newY); // 更新路径
// 继续深度优先搜索
dfs(book, data, visited, newX, newY, index + 1, M, path, N);
path.pop(); // 回溯,移除最后添加的路径
visited[newX][newY] = false; // 取消访问标记
}
}
}
function main() {
const input = prompt("请输入数据:").split("\n");
let inputIndex = 0;
// 读取明文长度
const N = parseInt(input[inputIndex++]);
// 读取明文数字序列
const data = input[inputIndex++].split(" ").map(Number);
// 读取密码本的大小
const M = parseInt(input[inputIndex++]);
// 读取密码本矩阵
let book = [];
for (let i = 0; i < M; i++) {
book.push(input[inputIndex++].split(" ").map(Number));
}
// 尝试从每一个位置开始搜索
for (let i = 0; i < M; i++) {
for (let j = 0; j < M; j++) {
// 如果当前单元格的值与明文的第一个数字匹配
if (book[i][j] === data[0]) {
let visited = Array.from({ length: M }, () => Array(M).fill(false)); // 记录访问过的单元格
visited[i][j] = true; // 标记当前单元格为访问过
// 从当前位置开始深度优先搜索
dfs(book, data, visited, i, j, 0, M, [`${i} ${j}`], N);
}
}
}
// 输出结果
console.log(minPath === null ? "error" : minPath);
}
// 调用主函数
main();
八、C算法源码
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define MAX_M 100
#define MAX_PATH_LEN 1000
int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
char minPath[MAX_PATH_LEN]; // 存储字典序最小的路径
void dfs(int book[MAX_M][MAX_M], int data[], bool visited[MAX_M][MAX_M], int x, int y, int index, int M, char path[], int N) {
// 如果已经匹配到明文的最后一个数字
if (index == N - 1) {
if (strlen(minPath) == 0 || strcmp(path, minPath) < 0) {
strcpy(minPath, path);
}
return;
}
// 遍历四个方向
for (int i = 0; i < 4; i++) {
int newX = x + dx[i];
int newY = y + dy[i];
// 检查新的位置是否在矩阵范围内,是否未访问过,且与明文的下一个数字匹配
if (newX >= 0 && newX < M && newY >= 0 && newY < M && !visited[newX][newY] && book[newX][newY] == data[index + 1]) {
visited[newX][newY] = true; // 标记新的位置为访问过
char newPath[MAX_PATH_LEN];
sprintf(newPath, "%s %d %d", path, newX, newY); // 更新路径
// 继续深度优先搜索
dfs(book, data, visited, newX, newY, index + 1, M, newPath, N);
visited[newX][newY] = false; // 取消访问标记
}
}
}
int main() {
int N, M;
int data[MAX_M];
int book[MAX_M][MAX_M];
bool visited[MAX_M][MAX_M] = { false }; // 访问记录矩阵
// 初始化 minPath
memset(minPath, 0, sizeof(minPath));
// 读取明文长度
scanf("%d", &N);
// 读取明文数字序列
for (int i = 0; i < N; i++) {
scanf("%d", &data[i]);
}
// 读取密码本的大小
scanf("%d", &M);
// 读取密码本矩阵
for (int i = 0; i < M; i++) {
for (int j = 0; j < M; j++) {
scanf("%d", &book[i][j]);
}
}
// 尝试从每一个位置开始搜索
for (int i = 0; i < M; i++) {
for (int j = 0; j < M; j++) {
// 如果当前单元格的值与明文的第一个数字匹配
if (book[i][j] == data[0]) {
visited[i][j] = true; // 标记当前单元格为访问过
char startPath[MAX_PATH_LEN];
sprintf(startPath, "%d %d", i, j);
// 从当前位置开始深度优先搜索
dfs(book, data, visited, i, j, 0, M, startPath, N);
visited[i][j] = false; // 取消访问标记
}
}
}
// 输出结果
if (strlen(minPath) == 0) {
printf("error\n");
} else {
printf("%s\n", minPath);
}
return 0;
}
九、C++算法源码
#include <iostream>
#include <vector>
#include <string>
#include <climits>
using namespace std;
vector<int> dx = {-1, 1, 0, 0};
vector<int> dy = {0, 0, -1, 1};
string minPath; // 存储字典序最小的路径
void dfs(const vector<vector<int>>& book, const vector<int>& data, vector<vector<bool>>& visited, int x, int y, int index, int M, string path, int N) {
// 如果已经匹配到明文的最后一个数字
if (index == N - 1) {
if (minPath.empty() || path < minPath) {
minPath = path;
}
return;
}
// 遍历四个方向
for (int i = 0; i < 4; i++) {
int newX = x + dx[i];
int newY = y + dy[i];
// 检查新的位置是否在矩阵范围内,是否未访问过,且与明文的下一个数字匹配
if (newX >= 0 && newX < M && newY >= 0 && newY < M && !visited[newX][newY] && book[newX][newY] == data[index + 1]) {
visited[newX][newY] = true; // 标记新的位置为访问过
dfs(book, data, visited, newX, newY, index + 1, M, path + " " + to_string(newX) + " " + to_string(newY), N);
visited[newX][newY] = false; // 取消访问标记
}
}
}
int main() {
int N, M;
cin >> N;
vector<int> data(N);
for (int i = 0; i < N; i++) {
cin >> data[i];
}
cin >> M;
vector<vector<int>> book(M, vector<int>(M));
for (int i = 0; i < M; i++) {
for (int j = 0; j < M; j++) {
cin >> book[i][j];
}
}
minPath = "";
// 尝试从每一个位置开始搜索
for (int i = 0; i < M; i++) {
for (int j = 0; j < M; j++) {
// 如果当前单元格的值与明文的第一个数字匹配
if (book[i][j] == data[0]) {
vector<vector<bool>> visited(M, vector<bool>(M, false)); // 记录访问过的单元格
visited[i][j] = true; // 标记当前单元格为访问过
dfs(book, data, visited, i, j, 0, M, to_string(i) + " " + to_string(j), N);
}
}
}
// 输出结果
if (minPath.empty()) {
cout << "error" << endl;
} else {
cout << minPath << endl;
}
return 0;
}
🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 2024 E卷 200分)
🏆本文收录于,华为OD机试真题(Python/JS/C/C++)
刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天CSDN在线答疑。