1 条题解

  • 0
    @ 2025-5-19 19:59:48

    题目描述

    我们需要从一个由数字('0'-'9')和字母('A'-'Z')组成的矩阵中找出隐藏的“秘密数字”。这个秘密数字是矩阵中所有可能的数字序列中的最大值,这些数字序列必须满足:

    1. 序列中的每个字符必须是数字(字母无效)。
    2. 序列中的每个数字 ( D_{k+1} ) 必须位于前一个数字 ( D_k ) 的正右侧或正下方(不能跳过或斜向移动)。
    3. 序列可以任意长度,但必须至少包含一个非零数字。
    4. 输出时,前导零需要被省略(例如,"023" 应输出为 "23")。

    示例分析

    输入样例1:

    7 4
    9R2A993
    0E314A0
    8A900DE
    820R037
    

    输出:

    23900037
    

    解释:

    • 矩阵中可能的数字序列包括 90882023140037239000379930
    • 其中最大的数字是 23900037,因此它是秘密数字。

    解题思路

    1. 深度优先搜索(DFS)+ 动态规划(DP)优化

      • 我们需要遍历矩阵中的每一个数字作为起点,然后递归搜索其右侧和下方的数字,构建所有可能的数字序列。
      • 由于矩阵可能较大(( W + H \leq 70 )),直接暴力搜索会导致指数级时间复杂度,因此需要优化:
        • 记忆化搜索(Memoization):存储已经计算过的位置的最优解,避免重复计算。
        • 剪枝(Pruning):如果当前路径的数字已经比已知的最大数字小,可以提前终止搜索。
      • 使用字符串比较来确保我们始终跟踪最大的数字序列。
    2. 关键步骤

      • 遍历矩阵:对每个数字字符('0'-'9')作为起点进行DFS。
      • DFS搜索:从当前位置出发,向右或向下移动,构建数字序列。
      • 比较和更新最大值:在DFS过程中,维护当前最大的数字序列。
      • 处理前导零:在最终输出时,跳过前导零。

    代码实现(c++)

    #include <iostream>
    #include <vector>
    #include <string>
    #include <algorithm>
    
    using namespace std;
    
    string max_num;
    vector<vector<string> > memo;
    
    void dfs(int i, int j, const vector<string>& grid, string current_str, int W, int H) {
        if (i < 0 || i >= H || j < 0 || j >= W) {
            return;
        }
        char c = grid[i][j];
        if (!isdigit(c)) {
            return;
        }
        string new_str = current_str + c;
        // Update max_num if new_str is larger
        if (new_str.length() > max_num.length() || 
            (new_str.length() == max_num.length() && new_str > max_num)) {
            max_num = new_str;
        }
        // Prune if memo[i][j] already has a better or equal solution
        if (memo[i][j] != "" && 
            (memo[i][j].length() > new_str.length() || 
                (memo[i][j].length() == new_str.length() && memo[i][j] >= new_str))) {
            return;
        }
        memo[i][j] = new_str;
        // Explore right and down
        dfs(i, j + 1, grid, new_str, W, H);
        dfs(i + 1, j, grid, new_str, W, H);
    }
    
    void solve() {
        while (true) {
            int W, H;
            cin >> W >> H;
            if (W == 0 && H == 0) {
                break;
            }
            vector<string> grid(H);
            for (int i = 0; i < H; ++i) {
                cin >> grid[i];
            }
            max_num = "";
            memo.assign(H, vector<string>(W, ""));
            for (int i = 0; i < H; ++i) {
                for (int j = 0; j < W; ++j) {
                    if (isdigit(grid[i][j])) {
                        dfs(i, j, grid, "", W, H);
                    }
                }
            }
            // Remove leading zeros
            if (!max_num.empty()) {
                size_t pos = max_num.find_first_not_of('0');
                if (pos == string::npos) {
                    max_num = "0";
                } else {
                    max_num = max_num.substr(pos);
                }
            }
            cout << max_num << endl;
        }
    }
    
    int main() {
        solve();
        return 0;
    }
    

    复杂度分析

    • 时间复杂度:最坏情况下 ( O(W \times H \times 2^{W+H}) ),但由于使用了记忆化剪枝,实际运行时间会大幅减少。
    • 空间复杂度:( O(W \times H \times L) ),其中 ( L ) 是最长数字序列的长度。

    优化点

    1. 记忆化存储:避免重复计算同一位置的最优解。
    2. 字符串比较剪枝:如果当前路径的数字比已知最大值短或更小,直接终止搜索。
    3. 前导零处理:在输出时去除前导零。

    总结

    本题的关键在于如何高效地遍历所有可能的数字序列,并找到最大的那个。DFS + 记忆化剪枝是一种有效的解法,能够避免暴力搜索的高时间复杂度。同时,需要注意前导零的处理,确保输出符合题目要求。

    • 1

    信息

    ID
    1031
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者