1007 学计算导致的

题目解析:计算合法表达式路径数目

题目概述

给定一个 \(n \times m\) 的网格,每个格子里填有 \(\{0,1,2,\dots,9,+,-,*\}\) 这 13 个字符之一。你需要从左上角出发,每次只能向右或向下移动一格,直至到达右下角,收集沿途经过的字符形成一个字符串。要求:

  1. 该字符串对应一个合法算式:即不能有两个连续的运算符(+, -, *),数字可以有前导零,但仍按十进制数看待。
  2. 算式的最终结果是 \(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\):当前正在拼接的数字。

解析时遵循以下规则:

  1. 读到数字 \(d\)(可能是多位)
    [\( cur \leftarrow (cur \times 10 + d) \mod k \)]
  2. 读到加号 +
    [\( sum \leftarrow (sum + mul \times cur) \mod k, \quad mul \leftarrow 1, \quad cur \leftarrow 0 \)]
  3. 读到减号 -
    [\( sum \leftarrow (sum + mul \times cur) \mod k, \quad mul \leftarrow (k - 1) \quad (\text{即} -1 \bmod k), \quad cur \leftarrow 0 \)]
  4. 读到乘号 *
    [\( mul \leftarrow (mul \times cur) \mod k, \quad cur \leftarrow 0 \)]
  5. 最终计算
    [\( 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,1)\) 处,令:
    [\( f(1,1,E(0,1, \text{digit})) = 1 \)]
    其中 E(sum, mul, cur) 是状态编码函数。

  2. 双重循环遍历网格 \((x,y)\),遍历所有状态 st

    • 若当前格子 ch 是数字或上一格是数字,则可以从 \((x-1,y)\) 转移。
    • 若当前格子 ch 是数字或左边格是数字,则可以从 \((x,y-1)\) 转移。
    • 累加到 f[x][y][tr[ch][st]]
  3. 终止条件

    • 在网格右下角外加一个 + 号,使最终 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;
}

结论

  1. 核心思想:状态机 + DP,确保合法性并快速计算模 \(k\)
  2. 优化技巧:状态压缩、预处理 tr[ch][st] 进行高效转移。
  3. 复杂度\(O(nm k^3)\),适用于 \(n, m \leq 100\)\(k \leq 20\) 的限制。

posted @ 2025-03-17 20:14  archer2333  阅读(44)  评论(0)    收藏  举报