1 条题解
-
0
题目描述
我们需要从一个由数字('0'-'9')和字母('A'-'Z')组成的矩阵中找出隐藏的“秘密数字”。这个秘密数字是矩阵中所有可能的数字序列中的最大值,这些数字序列必须满足:
- 序列中的每个字符必须是数字(字母无效)。
- 序列中的每个数字 ( D_{k+1} ) 必须位于前一个数字 ( D_k ) 的正右侧或正下方(不能跳过或斜向移动)。
- 序列可以任意长度,但必须至少包含一个非零数字。
- 输出时,前导零需要被省略(例如,"023" 应输出为 "23")。
示例分析
输入样例1:
7 4 9R2A993 0E314A0 8A900DE 820R037
输出:
23900037
解释:
- 矩阵中可能的数字序列包括
908820
、23140037
、23900037
和9930
。 - 其中最大的数字是
23900037
,因此它是秘密数字。
解题思路
-
深度优先搜索(DFS)+ 动态规划(DP)优化
- 我们需要遍历矩阵中的每一个数字作为起点,然后递归搜索其右侧和下方的数字,构建所有可能的数字序列。
- 由于矩阵可能较大(( W + H \leq 70 )),直接暴力搜索会导致指数级时间复杂度,因此需要优化:
- 记忆化搜索(Memoization):存储已经计算过的位置的最优解,避免重复计算。
- 剪枝(Pruning):如果当前路径的数字已经比已知的最大数字小,可以提前终止搜索。
- 使用字符串比较来确保我们始终跟踪最大的数字序列。
-
关键步骤
- 遍历矩阵:对每个数字字符('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 ) 是最长数字序列的长度。
优化点
- 记忆化存储:避免重复计算同一位置的最优解。
- 字符串比较剪枝:如果当前路径的数字比已知最大值短或更小,直接终止搜索。
- 前导零处理:在输出时去除前导零。
总结
本题的关键在于如何高效地遍历所有可能的数字序列,并找到最大的那个。DFS + 记忆化剪枝是一种有效的解法,能够避免暴力搜索的高时间复杂度。同时,需要注意前导零的处理,确保输出符合题目要求。
- 1
信息
- ID
- 1031
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者