华为OD机试 - 加密算法 - 深度优先搜索dfs(Python/JS/C/C++ 2025 B卷 200分)

在这里插入图片描述

2025B卷华为OD机试统一考试题库清单(持续收录中)以及考点说明(Python/JS/C/C++)

专栏导读

本专栏收录于《华为OD机试真题(Python/JS/C/C++)》

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。

一、题目描述

有一种特殊的加密算法,明文为一段数字串,经过密码本查找转换,生成另一段密文数字串。规则如下

  1. 明文为一段数字串由0-9组成
  2. 密码本为数字0-9组成的二维数组
  3. 需要按明文串的数字顺序在密码本里找到同样的数字串,密码本里的数字串是由相邻的单元格数字组成,上下和左右是相邻的,注意:对角线不相邻,同一个单元格的数字不能重复使用。
  4. 每一位明文对应密文即为密码本中找到的单元格所在的行和列序号(序号从0开始)组成的两个数字。如明文第i位Data[i]对应密码本单元格为Book[X][Y],则明文第i位对应的密文为X Y,X和Y之间用空格隔开。如果有多条密文,返回字符序最小的密文。如果密码本无法匹配,返回"error".

请你设计这个加密程序。

示例 1:

密码本:

{0,0,2},
{1,3,4},
{6,6,4}

明文"3",密文"1 1"

示例 2:

密码本:

{0,0,2},
{1,3,4},
{6,6,4}

明文"0 3",密文"0 1 1 1"

二、输入描述

第一行输入1个正整数N,代表明文的长度(1 <= N <= 9)

第二行输入N个明文数字组成的序列Data[i](整数,0 <= Data[i] <= 9)

第三行输入1个正整数M,(1 <= M <= 9)

接下来输入一个M*M的矩阵代表密码本Book[i][i],(整数,0 <= Book[i][i] <= 9)

三、输出描述

如明文 第i位Data[i]对应密码本单元格为Book[i][j],则明文第i位对应的密文为X Y,X和Y之间用空格隔开。如果有多条密文,返回字符序最小的密文。如果密码本无法匹配,返回"error"。

四、测试用例

测试用例1

1、输入

1
3
3
0 0 2
1 3 4
6 6 4

2、输出

1 1

3、说明

明文只有一个数字"3",在密码本中位置为(1,1),所以密文为"1 1"。

测试用例2

1、输入

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

2、输出

error

3、说明

明文为"5 7",在密码本中可以找到"5",但没有与"5"相邻的"7",所以无法找到匹配路径,返回"error"。

五、解题思路

1、解题思路

首先在密码本中找到所有与明文第一个数字匹配的单元格作为起点。
从每个起点开始,使用DFS搜索与明文匹配的完整路径。
对于每个有效路径,构建对应的密文(单元格坐标序列)。
如果找到多个可能的密文,按字典序排序并返回最小的一个。
如果没有找到匹配的路径,返回"error"。

2、数据结构和算法选择

二维数组(密码本):用于存储密码本的数据。

深度优先搜索(DFS):用于在密码本中寻找匹配明文的路径。

回溯算法:在DFS中使用回溯来尝试所有可能的路径。

3、为什么采用这些数据结构和算法

二维数组:密码本本身就是一个MxM的矩阵,使用二维数组最为直观和高效。

深度优先搜索(DFS):

  1. 适合在网格中寻找路径的问题。
  2. 能够探索所有可能的方向(上、右、下、左)。
  3. 结合回溯可以穷举所有可能的路径。

回溯算法:当发现当前路径不满足条件时,可以回退到上一步,尝试其他可能的路径。对于搜索所有可能解的问题非常有效。

六、Python算法源码

def main():
    # 读取明文长度 N
    N = int(input())
    
    # 读取明文数字序列
    plaintext = list(map(int, input().split()))
    
    # 读取密码本大小 M
    M = int(input())
    
    # 读取密码本
    codeBook = []
    for _ in range(M):
        codeBook.append(list(map(int, input().split())))
    
    # 四个方向:上、右、下、左
    dr = [-1, 0, 1, 0]  # 行变化
    dc = [0, 1, 0, -1]  # 列变化
    
    # 找到所有可能的密文
    def findAllCiphertexts():
        allCiphertexts = []
        
        # 遍历密码本中的每一个单元格作为起始点
        for i in range(M):
            for j in range(M):
                # 如果起始单元格匹配明文的第一个数字
                if codeBook[i][j] == plaintext[0]:
                    # 记录已访问的单元格
                    visited = [[False for _ in range(M)] for _ in range(M)]
                    visited[i][j] = True
                    
                    # 记录当前路径
                    path = [(i, j)]
                    
                    # 开始深度优先搜索
                    dfs(1, i, j, visited, path, allCiphertexts)
        
        return allCiphertexts
    
    # 使用DFS搜索匹配明文的路径
    def dfs(index, r, c, visited, path, allCiphertexts):
        # 如果已经匹配了整个明文
        if index == len(plaintext):
            # 构建密文字符串
            ciphertext = ""
            for i in range(len(path)):
                coord = path[i]
                ciphertext += str(coord[0]) + " " + str(coord[1])
                if i < len(path) - 1:
                    ciphertext += " "
            # 添加到密文列表
            allCiphertexts.append(ciphertext)
            return
        
        # 尝试四个方向的相邻单元格
        for d in range(4):
            nr = r + dr[d]
            nc = c + dc[d]
            
            # 检查新单元格是否在密码本范围内,且未被访问过,且匹配明文当前字符
            if 0 <= nr < M and 0 <= nc < M and not visited[nr][nc] and codeBook[nr][nc] == plaintext[index]:
                # 标记为已访问
                visited[nr][nc] = True
                # 添加到当前路径
                path.append((nr, nc))
                
                # 继续搜索下一个字符
                dfs(index + 1, nr, nc, visited, path, allCiphertexts)
                
                # 回溯
                path.pop()
                visited[nr][nc] = False
    
    allCiphertexts = findAllCiphertexts()
    
    if not allCiphertexts:
        # 如果没有找到匹配的密文,返回 "error"
        print("error")
    else:
        # 按字典序排序,返回最小的密文
        allCiphertexts.sort()
        print(allCiphertexts[0])

if __name__ == "__main__":
    main()

七、JavaScript算法源码

// 使用Node.js运行JavaScript代码,通过process.stdin和process.stdout进行输入输出
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

const lines = [];
let lineIndex = 0;

rl.on('line', (line) => {
    lines.push(line);
});

rl.on('close', () => {
    // 读取明文长度 N
    const N = parseInt(lines[lineIndex++]);
    
    // 读取明文数字序列
    const plaintext = lines[lineIndex++].split(' ').map(Number);
    
    // 读取密码本大小 M
    const M = parseInt(lines[lineIndex++]);
    
    // 读取密码本
    const codeBook = [];
    for (let i = 0; i < M; i++) {
        codeBook.push(lines[lineIndex++].split(' ').map(Number));
    }
    
    // 四个方向:上、右、下、左
    const dr = [-1, 0, 1, 0];  // 行变化
    const dc = [0, 1, 0, -1];  // 列变化
    
    // 找到所有可能的密文
    function findAllCiphertexts() {
        const allCiphertexts = [];
        
        // 遍历密码本中的每一个单元格作为起始点
        for (let i = 0; i < M; i++) {
            for (let j = 0; j < M; j++) {
                // 如果起始单元格匹配明文的第一个数字
                if (codeBook[i][j] === plaintext[0]) {
                    // 记录已访问的单元格
                    const visited = Array(M).fill().map(() => Array(M).fill(false));
                    visited[i][j] = true;
                    
                    // 记录当前路径
                    const path = [[i, j]];
                    
                    // 开始深度优先搜索
                    dfs(1, i, j, visited, path, allCiphertexts);
                }
            }
        }
        
        return allCiphertexts;
    }
    
    // 使用DFS搜索匹配明文的路径
    function dfs(index, r, c, visited, path, allCiphertexts) {
        // 如果已经匹配了整个明文
        if (index === plaintext.length) {
            // 构建密文字符串
            let ciphertext = "";
            for (let i = 0; i < path.length; i++) {
                const coord = path[i];
                ciphertext += coord[0] + " " + coord[1];
                if (i < path.length - 1) {
                    ciphertext += " ";
                }
            }
            // 添加到密文列表
            allCiphertexts.push(ciphertext);
            return;
        }
        
        // 尝试四个方向的相邻单元格
        for (let d = 0; d < 4; d++) {
            const nr = r + dr[d];
            const nc = c + dc[d];
            
            // 检查新单元格是否在密码本范围内,且未被访问过,且匹配明文当前字符
            if (nr >= 0 && nr < M && nc >= 0 && nc < M && !visited[nr][nc] && codeBook[nr][nc] === plaintext[index]) {
                // 标记为已访问
                visited[nr][nc] = true;
                // 添加到当前路径
                path.push([nr, nc]);
                
                // 继续搜索下一个字符
                dfs(index + 1, nr, nc, visited, path, allCiphertexts);
                
                // 回溯
                path.pop();
                visited[nr][nc] = false;
            }
        }
    }
    
    const allCiphertexts = findAllCiphertexts();
    
    if (allCiphertexts.length === 0) {
        // 如果没有找到匹配的密文,返回 "error"
        console.log("error");
    } else {
        // 按字典序排序,返回最小的密文
        allCiphertexts.sort();
        console.log(allCiphertexts[0]);
    }
});

八、C算法源码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

// 四个方向:上、右、下、左
int dr[4] = {-1, 0, 1, 0};  // 行变化
int dc[4] = {0, 1, 0, -1};  // 列变化

// 密文信息结构体
typedef struct {
    char* text;
} Ciphertext;

// 路径信息结构体
typedef struct {
    int* row;
    int* col;
    int size;
    int capacity;
} Path;

// 初始化路径
Path* createPath(int capacity) {
    Path* path = (Path*)malloc(sizeof(Path));
    path->row = (int*)malloc(sizeof(int) * capacity);
    path->col = (int*)malloc(sizeof(int) * capacity);
    path->size = 0;
    path->capacity = capacity;
    return path;
}

// 释放路径内存
void freePath(Path* path) {
    free(path->row);
    free(path->col);
    free(path);
}

// 添加坐标到路径
void addToPath(Path* path, int r, int c) {
    if (path->size >= path->capacity) {
        path->capacity *= 2;
        path->row = (int*)realloc(path->row, sizeof(int) * path->capacity);
        path->col = (int*)realloc(path->col, sizeof(int) * path->capacity);
    }
    path->row[path->size] = r;
    path->col[path->size] = c;
    path->size++;
}

// 从路径中移除最后一个坐标
void removeLastFromPath(Path* path) {
    if (path->size > 0) {
        path->size--;
    }
}

// 构建密文字符串
char* buildCiphertext(Path* path) {
    char* ciphertext = (char*)malloc(path->size * 10); // 足够大的空间
    ciphertext[0] = '\0';
    
    for (int i = 0; i < path->size; i++) {
        char buffer[10];
        sprintf(buffer, "%d %d", path->row[i], path->col[i]);
        strcat(ciphertext, buffer);
        if (i < path->size - 1) {
            strcat(ciphertext, " ");
        }
    }
    
    return ciphertext;
}

// 比较两个密文字符串
int compareCiphertexts(const void* a, const void* b) {
    return strcmp((*(Ciphertext*)a).text, (*(Ciphertext*)b).text);
}

// DFS搜索匹配明文的路径
void dfs(int* plaintext, int plaintext_len, int** codeBook, int M, int index, int r, int c, bool** visited, Path* path, Ciphertext** allCiphertexts, int* ciphertext_count, int* capacity) {
    // 如果已经匹配了整个明文
    if (index == plaintext_len) {
        // 构建密文字符串
        char* ciphertext = buildCiphertext(path);
        
        // 添加到密文列表
        if (*ciphertext_count >= *capacity) {
            *capacity *= 2;
            *allCiphertexts = (Ciphertext*)realloc(*allCiphertexts, sizeof(Ciphertext) * (*capacity));
        }
        (*allCiphertexts)[*ciphertext_count].text = ciphertext;
        (*ciphertext_count)++;
        
        return;
    }
    
    // 尝试四个方向的相邻单元格
    for (int d = 0; d < 4; d++) {
        int nr = r + dr[d];
        int nc = c + dc[d];
        
        // 检查新单元格是否在密码本范围内,且未被访问过,且匹配明文当前字符
        if (nr >= 0 && nr < M && nc >= 0 && nc < M && !visited[nr][nc] && codeBook[nr][nc] == plaintext[index]) {
            // 标记为已访问
            visited[nr][nc] = true;
            // 添加到当前路径
            addToPath(path, nr, nc);
            
            // 继续搜索下一个字符
            dfs(plaintext, plaintext_len, codeBook, M, index + 1, nr, nc, visited, path, allCiphertexts, ciphertext_count, capacity);
            
            // 回溯
            removeLastFromPath(path);
            visited[nr][nc] = false;
        }
    }
}

// 找到所有可能的密文
Ciphertext* findAllCiphertexts(int* plaintext, int plaintext_len, int** codeBook, int M, int* ciphertext_count) {
    int capacity = 100;
    Ciphertext* allCiphertexts = (Ciphertext*)malloc(sizeof(Ciphertext) * capacity);
    *ciphertext_count = 0;
    
    // 遍历密码本中的每一个单元格作为起始点
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < M; j++) {
            // 如果起始单元格匹配明文的第一个数字
            if (codeBook[i][j] == plaintext[0]) {
                // 记录已访问的单元格
                bool** visited = (bool**)malloc(sizeof(bool*) * M);
                for (int k = 0; k < M; k++) {
                    visited[k] = (bool*)calloc(M, sizeof(bool));
                }
                visited[i][j] = true;
                
                // 记录当前路径
                Path* path = createPath(plaintext_len);
                addToPath(path, i, j);
                
                // 开始深度优先搜索
                dfs(plaintext, plaintext_len, codeBook, M, 1, i, j, visited, path, &allCiphertexts, ciphertext_count, &capacity);
                
                // 释放内存
                freePath(path);
                for (int k = 0; k < M; k++) {
                    free(visited[k]);
                }
                free(visited);
            }
        }
    }
    
    return allCiphertexts;
}

int main() {
    // 读取明文长度 N
    int N;
    scanf("%d", &N);
    
    // 读取明文数字序列
    int* plaintext = (int*)malloc(sizeof(int) * N);
    for (int i = 0; i < N; i++) {
        scanf("%d", &plaintext[i]);
    }
    
    // 读取密码本大小 M
    int M;
    scanf("%d", &M);
    
    // 读取密码本
    int** codeBook = (int**)malloc(sizeof(int*) * M);
    for (int i = 0; i < M; i++) {
        codeBook[i] = (int*)malloc(sizeof(int) * M);
        for (int j = 0; j < M; j++) {
            scanf("%d", &codeBook[i][j]);
        }
    }
    
    // 找到所有可能的密文
    int ciphertext_count;
    Ciphertext* allCiphertexts = findAllCiphertexts(plaintext, N, codeBook, M, &ciphertext_count);
    
    if (ciphertext_count == 0) {
        // 如果没有找到匹配的密文,返回 "error"
        printf("error\n");
    } else {
        // 按字典序排序,返回最小的密文
        qsort(allCiphertexts, ciphertext_count, sizeof(Ciphertext), compareCiphertexts);
        printf("%s\n", allCiphertexts[0].text);
    }
    
    // 释放内存
    for (int i = 0; i < ciphertext_count; i++) {
        free(allCiphertexts[i].text);
    }
    free(allCiphertexts);
    
    for (int i = 0; i < M; i++) {
        free(codeBook[i]);
    }
    free(codeBook);
    free(plaintext);
    
    return 0;
}

九、C++算法源码

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <queue>

using namespace std;

// 四个方向:上、右、下、左
const int dr[4] = {-1, 0, 1, 0};  // 行变化
const int dc[4] = {0, 1, 0, -1};  // 列变化

// 使用DFS搜索匹配明文的路径
void dfs(const vector<int>& plaintext, const vector<vector<int>>& codeBook, int M, int index, int r, int c, 
         vector<vector<bool>>& visited, vector<pair<int, int>>& path, vector<string>& allCiphertexts) {
    // 如果已经匹配了整个明文
    if (index == plaintext.size()) {
        // 构建密文字符串
        string ciphertext = "";
        for (int i = 0; i < path.size(); i++) {
            ciphertext += to_string(path[i].first) + " " + to_string(path[i].second);
            if (i < path.size() - 1) {
                ciphertext += " ";
            }
        }
        // 添加到密文列表
        allCiphertexts.push_back(ciphertext);
        return;
    }
    
    // 尝试四个方向的相邻单元格
    for (int d = 0; d < 4; d++) {
        int nr = r + dr[d];
        int nc = c + dc[d];
        
        // 检查新单元格是否在密码本范围内,且未被访问过,且匹配明文当前字符
        if (nr >= 0 && nr < M && nc >= 0 && nc < M && !visited[nr][nc] && codeBook[nr][nc] == plaintext[index]) {
            // 标记为已访问
            visited[nr][nc] = true;
            // 添加到当前路径
            path.push_back({nr, nc});
            
            // 继续搜索下一个字符
            dfs(plaintext, codeBook, M, index + 1, nr, nc, visited, path, allCiphertexts);
            
            // 回溯
            path.pop_back();
            visited[nr][nc] = false;
        }
    }
}

// 找到所有可能的密文
vector<string> findAllCiphertexts(const vector<int>& plaintext, const vector<vector<int>>& codeBook, int M) {
    vector<string> allCiphertexts;
    
    // 遍历密码本中的每一个单元格作为起始点
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < M; j++) {
            // 如果起始单元格匹配明文的第一个数字
            if (codeBook[i][j] == plaintext[0]) {
                // 记录已访问的单元格
                vector<vector<bool>> visited(M, vector<bool>(M, false));
                visited[i][j] = true;
                
                // 记录当前路径
                vector<pair<int, int>> path;
                path.push_back({i, j});
                
                // 开始深度优先搜索
                dfs(plaintext, codeBook, M, 1, i, j, visited, path, allCiphertexts);
            }
        }
    }
    
    return allCiphertexts;
}

int main() {
    // 读取明文长度 N
    int N;
    cin >> N;
    
    // 读取明文数字序列
    vector<int> plaintext(N);
    for (int i = 0; i < N; i++) {
        cin >> plaintext[i];
    }
    
    // 读取密码本大小 M
    int M;
    cin >> M;
    
    // 读取密码本
    vector<vector<int>> codeBook(M, vector<int>(M));
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < M; j++) {
            cin >> codeBook[i][j];
        }
    }
    
    // 找到所有可能的密文
    vector<string> allCiphertexts = findAllCiphertexts(plaintext, codeBook, M);
    
    if (allCiphertexts.empty()) {
        // 如果没有找到匹配的密文,返回 "error"
        cout << "error" << endl;
    } else {
        // 按字典序排序,返回最小的密文
        sort(allCiphertexts.begin(), allCiphertexts.end());
        cout << allCiphertexts[0] << endl;
    }
    
    return 0;
}

🏆下一篇:华为OD机试真题 - 简易内存池(Python/JS/C/C++ 2025 B卷 200分)

🏆本文收录于,华为OD机试真题(Python/JS/C/C++)

刷的越多,抽中的概率越大,私信哪吒,备注华为OD,加入华为OD刷题交流群,每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新。

在这里插入图片描述

### 华为OD糖果问题的Python解题思路 #### 问题描述 小明从糖果盒中随意抓取一定数量的糖果,每次可以执行三种操作之一:取出一半糖果并舍去余数;如果当前糖果数目为奇数,则可以从盒子中再拿一个糖果或将手里的一个糖果放回去。目标是最少经过几次这样的操作能够使手中的糖果变为一颗。 为了达到这个目的,采用贪心策略来解决问题[^3]。具体来说,在每一步都尽可能快速地减少糖果的数量直到只剩下一颗为止。当遇到奇数个糖果时,优先考虑加一而不是减一,因为这样可以在下一轮更有效地通过除以2的方式大量削减糖果总数。 #### 贪婪算法析 对于任意正整数n表示初始糖果数: - 如果 n 是偶数,则直接将其除以2; - 若 n 为奇数,则判断 (n+1)/2 和 (n−1)/2 的大小关系,选择较小的那个作为下一步的结果,这是因为我们希望更快接近1。 这种做法基于这样一个事实——当我们面对两个连续的奇数值时,较大的那个总是可以通过增加1变成一个小得多的新值(即其半数),而较小的一个则需要两次操作才能完成同样的效果(先减后除)[^4]。 #### Python代码实现 下面给出了解决上述问题的具体Python程序: ```python def min_steps_to_one(n): steps = 0 while n != 1: if n % 2 == 0: n //= 2 elif ((n + 1) // 2) < ((n - 1) // 2): n += 1 else: n -= 1 steps += 1 return steps if __name__ == "__main__": candy_count = int(input().strip()) print(min_steps_to_one(candy_count)) ``` 此函数接收一个参数`n`代表起始的糖果数量,并返回将这些糖果减少到只有一个所需的最小步数。主程序部读入用户输入的数据并调用该方法打印最终结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

哪 吒

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

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

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

打赏作者

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

抵扣说明:

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

余额充值