📌  相关文章
📜  从给定矩阵的左上到右下最大化乘积的尾随零(1)

📅  最后修改于: 2023-12-03 15:22:02.198000             🧑  作者: Mango

从给定矩阵的左上到右下最大化乘积的尾随零

这是一个基于矩阵最大路径问题的变体,要求找到从矩阵的左上角到右下角的一条路径,使得路径上的所有数的乘积的尾随零最多。具体地,我们要求找到一个数列 $(a_1, a_2, ..., a_k)$,满足 $a_1 = A_{1,1}$,$a_k = A_{n,m}$,且 $10^p$ 是 $\prod_{i=1}^k a_i$ 的尾随零的个数。

解题思路

对于任意一个数 $x$,其尾随零的个数可以表示为其所包含的因子 $2$ 和 $5$ 的个数中较小的那个数。因此,我们需要尽可能地提取出 $a_1, a_2, ..., a_k$ 中的因子 $2$ 和 $5$,使得相乘后的结果中尾随零的个数最多。

一种经典的做法是通过动态规划来解决该问题。我们设 $f(i,j)$ 表示从 $(1,1)$ 到 $(i,j)$ 的一条路径,使得路径上的所有数乘积的尾随零数最多的那个路径所包含的数的总个数。由于我们要考虑尾随零的个数,那么我们需要同时考虑 $2$ 和 $5$ 这两个因子。我们可以使用对数函数来统计其中的 $2$ 和 $5$ 的个数,然后取较小值即可。

对于 $f(i,j)$ 的求解,我们可以考虑路径的来源。显然,$f(i,j)$ 可以从 $f(i-1,j)$ 或 $f(i,j-1)$ 转移而来。假设 $f(i,j)$ 从 $f(i-1,j)$ 转移而来,那么我们需要在到达 $(i-1,j)$ 时尽可能地提取出 $A_{i-1,j}$ 中的因子 $2$ 和 $5$,之后再乘上 $A_{i,j}$,就可以得到到达 $(i,j)$ 时尽可能提取出因子 $2$ 和 $5$ 的路径了。因此,我们可以得到如下的转移方程:

$$f(i,j) = \max{\min{f(i-1,j),\ f(i,j-1)} + (\mathrm{log}_2 \cdot \mathrm{log}5)(A{i,j})}$$

其中 $\mathrm{log}_2(a)$ 和 $\mathrm{log}_5(a)$ 分别表示 $a$ 中包含的因子 $2$ 和 $5$ 的个数。初始值为 $f(1,1) = (\mathrm{log}_2 \cdot \mathrm{log}5)(A{1,1})$。

当然,另一种更加简洁的做法是直接记录 $f(i,j)$ 中尾随零的个数即可,不必考虑因子 $2$ 和 $5$。

代码实现

下面给出基于尾随零个数的做法的参考代码,时间复杂度为 $O(nm)$:

def trailing_zeros(n):
    """计算 n 的尾随零的个数"""
    cnt = 0
    while n > 0 and n % 5 == 0:
        cnt += 1
        n //= 5
    return cnt

def max_trailing_zeros(matrix):
    """找到从矩阵左上角到右下角的路径,使得路径上的所有数乘积的尾随零最多"""
    n, m = len(matrix), len(matrix[0])
    f = [[0] * m for _ in range(n)]
    f[0][0] = trailing_zeros(matrix[0][0])

    for i in range(1, n):
        f[i][0] = f[i-1][0] + trailing_zeros(matrix[i][0])

    for j in range(1, m):
        f[0][j] = f[0][j-1] + trailing_zeros(matrix[0][j])

    for i in range(1, n):
        for j in range(1, m):
            f[i][j] = max(f[i-1][j], f[i][j-1]) + trailing_zeros(matrix[i][j])

    return f[n-1][m-1]

注意,上述代码中的 trailing_zeros(n) 函数可以通过类似于判断一个数的素因子个数的方法来实现,代码比较简单。如果想要进一步优化时间复杂度,可以使用查表法来减小常数,或者直接改用 $2$ 和 $5$ 的幂来计算。