1007 学计算导致的
题目解析:计算合法表达式路径数目
题目概述
给定一个 \(n \times m\) 的网格,每个格子里填有 \(\{0,1,2,\dots,9,+,-,*\}\) 这 13 个字符之一。你需要从左上角出发,每次只能向右或向下移动一格,直至到达右下角,收集沿途经过的字符形成一个字符串。要求:
- 该字符串对应一个合法算式:即不能有两个连续的运算符(
+,-,*),数字可以有前导零,但仍按十进制数看待。 - 算式的最终结果是 \(k\) 的倍数,并对答案取模 \(10^9 + 7\)。
保证左上角和右下角的格子一定是数字字符。
示例分析
输入示例:
1
3 4 3
1+07
4-2*
22-9
可能的合法路径:
1+07-2-9→ 结果 \(= -3\),是 \(3\) 的倍数 ✅1+07-29→ 结果 \(= -21\),是 \(3\) 的倍数 ✅1+072-9→ 结果 \(= 64\),不是 \(3\) 的倍数 ❌107-2-9→ 结果 \(= 96\),不是 \(3\) 的倍数 ❌4-2*22-9→ 结果 \(= 31\),不是 \(3\) 的倍数 ❌
最终统计符合要求的路径数目为 \(4\)。
思路分析
1. 表达式解析
考虑从左上角到右下角收集的字符组成一个算式,如何保证它是合法的,并能正确计算结果对 \(k\) 取模。
我们将表达式计算拆分为 三部分:
- \(sum\):当前已经累加的总和(加减部分的结果)。
- \(mul\):当前正在乘的因子前的符号/系数(遇到
+或-时会把之前的乘积部分加入到 \(sum\) 中,然后重置 \(mul\))。 - \(cur\):当前正在拼接的数字。
解析时遵循以下规则:
- 读到数字 \(d\)(可能是多位):
[\( cur \leftarrow (cur \times 10 + d) \mod k \)] - 读到加号
+:
[\( sum \leftarrow (sum + mul \times cur) \mod k, \quad mul \leftarrow 1, \quad cur \leftarrow 0 \)] - 读到减号
-:
[\( sum \leftarrow (sum + mul \times cur) \mod k, \quad mul \leftarrow (k - 1) \quad (\text{即} -1 \bmod k), \quad cur \leftarrow 0 \)] - 读到乘号
*:
[\( mul \leftarrow (mul \times cur) \mod k, \quad cur \leftarrow 0 \)] - 最终计算:
[\( res = (sum + mul \times cur) \mod k \)]
当 \(res \equiv 0 \pmod{k}\) 时,表示该表达式的值是 \(k\) 的倍数。
2. 状态设计与 DP 递推
(1)状态定义
用三元组 \((sum, mul, cur)\) 记录当前算式的计算状态,其中:
- \(sum, mul, cur \in [0, k-1]\)。
我们可以用一个哈希映射(或整数编码)将三元组转换为一个状态 ID,方便 DP 计算。
(2)DP 数组定义
令:
[\(
f(x, y, st)
\)]
表示走到网格坐标 \((x, y)\) 时,所处的三元组状态为 \(st\) 时的路径数量。
状态转移:
- 若当前格子是数字,则上一格可以是数字或运算符;
- 若当前格子是运算符,则上一格必须是数字。
设 tr[ch][st] 为字符 ch 作用于状态 st 后的新状态,则:
[\(
f(x,y,\text{new\_st}) += f(\text{上一个格子},\, \text{旧状态 st})
\)]
其中:
[\(
\text{new\_st} = tr[ch][st]
\)]
3. DP 计算框架
-
初始化:在 \((1,1)\) 处,令:
[\( f(1,1,E(0,1, \text{digit})) = 1 \)]
其中E(sum, mul, cur)是状态编码函数。 -
双重循环遍历网格 \((x,y)\),遍历所有状态
st:- 若当前格子
ch是数字或上一格是数字,则可以从 \((x-1,y)\) 转移。 - 若当前格子
ch是数字或左边格是数字,则可以从 \((x,y-1)\) 转移。 - 累加到
f[x][y][tr[ch][st]]。
- 若当前格子
-
终止条件:
- 在网格右下角外加一个
+号,使最终cur并入sum,从而只需检查:
[\( sum = 0, \quad mul = 1, \quad cur = 0 \)] - 结果即:
[\( f(n, m+1, E(0,1,0)) \mod (10^9 + 7). \)]
- 在网格右下角外加一个
代码框架
// C++ 代码示例
const int MOD = 1e9 + 7;
int f[MAX_N][MAX_M][STATE_COUNT];
void solve() {
cin >> n >> m >> k;
vector<string> grid(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> grid[i];
grid[i] = " " + grid[i]; // 1-based
}
grid[n][m+1] = '+';
memset(f, 0, sizeof(f));
f[1][1][E(0, 1, grid[1][1] - '0')] = 1;
for (int x = 1; x <= n; ++x) {
for (int y = 1; y <= m; ++y) {
for (int st = 0; st < STATE_COUNT; ++st) {
if (!f[x][y][st]) continue;
for (char ch : {'+', '-', '*', '0', '1', ..., '9'}) {
int new_st = tr[ch][st];
f[x+1][y][new_st] = (f[x+1][y][new_st] + f[x][y][st]) % MOD;
f[x][y+1][new_st] = (f[x][y+1][new_st] + f[x][y][st]) % MOD;
}
}
}
}
cout << f[n][m+1][E(0,1,0)] << endl;
}
结论
- 核心思想:状态机 + DP,确保合法性并快速计算模 \(k\)。
- 优化技巧:状态压缩、预处理
tr[ch][st]进行高效转移。 - 复杂度:\(O(nm k^3)\),适用于 \(n, m \leq 100\),\(k \leq 20\) 的限制。

浙公网安备 33010602011771号