题目

给你一个二维整数数组 grid ,大小为 m x n,其中每个单元格都含一个正整数。

转角路径 定义为:包含至多一个弯的一组相邻单元。具体而言,路径应该完全 向水平方向 或者 向竖直方向 移动过弯(如果存在弯),而不能访问之前访问过的单元格。在过弯之后,路径应当完全朝 另一个 方向行进:如果之前是向水平方向,那么就应该变为向竖直方向;反之亦然。当然,同样不能访问之前已经访问过的单元格。

一条路径的 乘积 定义为:路径上所有值的乘积。

请你从 grid 中找出一条乘积中尾随零数目最多的转角路径,并返回该路径中尾随零的数目。

注意:

水平 移动是指向左或右移动。
竖直 移动是指向上或下移动。

示例 1: image.png

输入:grid = [[23,17,15,3,20],[8,1,20,27,11],[9,4,6,2,21],[40,9,1,10,6],[22,7,4,5,3]]
输出:3
解释:左侧的图展示了一条有效的转角路径。
其乘积为 15 20 6 1 10 = 18000 ,共计 3 个尾随零。
可以证明在这条转角路径的乘积中尾随零数目最多。

中间的图不是一条有效的转角路径,因为它有不止一个弯。
右侧的图也不是一条有效的转角路径,因为它需要重复访问已经访问过的单元格。

示例 2:

image.png

输入:grid = [[4,3,2],[7,6,1],[8,8,8]]
输出:0
解释:网格如上图所示。
不存在乘积含尾随零的转角路径。

提示:

m == grid.length
n == grid[i].length
1 <= m, n <= 10^5
1 <= m * n <= 10^5
1 <= grid[i][j] <= 1000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-trailing-zeros-in-a-cornered-path
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

第一反应是dfs,也因为做过172题知道尾0就是找每个数2和5的因子个数,个数较小的数目就是0的个数。不过就是只能转一次弯不好处理,最后觉得代码没问题了还是过不去…

比赛结束后知道原来正确的做法不是dfs,正确的做法是枚举拐点,选择两个方向,有四种可能,分别求尾0的个数,因为要求矩阵某一水平或竖直线段内2或者5的因子个数,所以可以先使用前缀和数组记录每行和每列2和5的因子数目,之后可以O(1)时间求得某一水平或竖直线段内2或者5的因子个数。

虽然思想不难,但不得不说,代码是真的不好写…一个下标错了就错了

代码

  1. class Solution {
  2. public int maxTrailingZeros(int[][] grid) {
  3. int m = grid.length;
  4. int n = grid[0].length;
  5. // 每行的前缀和数组,f2[i,:]表示grid第i-1行2因子个数的前缀和数组,其中i属于[1,m],f5同理
  6. int[][] f2 = new int[m + 1][n + 1];
  7. // 每列的前缀和数组,g2[:,j]表示grid第j-1列2因子个数的前缀和数组,其中j属于[1,n],g5同理
  8. int[][] g2 = new int[m + 1][n + 1];
  9. int[][] f5 = new int[m + 1][n + 1];
  10. int[][] g5 = new int[m + 1][n + 1];
  11. for (int i = 1; i <= m; i++) {
  12. for (int j = 1; j <= n; j++) {
  13. int num = grid[i - 1][j - 1];
  14. // num 2的因子个数
  15. int two = 0;
  16. // num 5的因子个数
  17. int five = 0;
  18. while (num % 2 == 0) {
  19. num /= 2;
  20. two++;
  21. }
  22. while (num % 5 == 0) {
  23. num /= 5;
  24. five++;
  25. }
  26. f2[i][j] = f2[i][j - 1] + two;
  27. f5[i][j] = f5[i][j - 1] + five;
  28. g2[i][j] = g2[i - 1][j] + two;
  29. g5[i][j] = g5[i - 1][j] + five;
  30. }
  31. }
  32. int ans = 0;
  33. for (int i = 1; i <= m; i++) {
  34. for (int j = 1; j <= n; j++) {
  35. // 上左,内层Math.min表示尾0个数取决于2和5因子数目更少的那一个,然后外层Math.max更新ans
  36. ans = Math.max(ans, Math.min(g2[i][j] + f2[i][j - 1], g5[i][j] + f5[i][j - 1]));
  37. // 上右
  38. ans = Math.max(ans, Math.min(g2[i][j] + f2[i][n] - f2[i][j], g5[i][j] + f5[i][n] - f5[i][j]));
  39. // 左下
  40. ans = Math.max(ans, Math.min(f2[i][j] + g2[m][j] - g2[i][j], f5[i][j] + g5[m][j] - g5[i][j]));
  41. // 右下
  42. ans = Math.max(ans, Math.min(f2[i][n] - f2[i][j - 1] + g2[m][j] - g2[i][j], f5[i][n] - f5[i][j - 1] + g5[m][j] - g5[i][j]));
  43. }
  44. }
  45. return ans;
  46. }
  47. }