|
| 1 | +--- |
| 2 | +title: 1878.矩阵中最大的三个菱形和:斜向前缀和 |
| 3 | +date: 2026-03-17 13:31:48 |
| 4 | +tags: [题解, LeetCode, 中等, 数组, 数学, 矩阵, 前缀和, 排序, 堆(优先队列)] |
| 5 | +categories: [题解, LeetCode] |
| 6 | +index_img: https://assets.leetcode.com/uploads/2021/04/23/pc73-q4-desc-2.png |
| 7 | +--- |
| 8 | + |
| 9 | +# 【LetMeFly】1878.矩阵中最大的三个菱形和:斜向前缀和 |
| 10 | + |
| 11 | +力扣题目链接:[https://leetcode.cn/problems/get-biggest-three-rhombus-sums-in-a-grid/](https://leetcode.cn/problems/get-biggest-three-rhombus-sums-in-a-grid/) |
| 12 | + |
| 13 | +<p>给你一个 <code>m x n</code> 的整数矩阵 <code>grid</code> 。</p> |
| 14 | + |
| 15 | +<p><strong>菱形和</strong> 指的是 <code>grid</code> 中一个正菱形 <strong>边界</strong> 上的元素之和。本题中的菱形必须为正方形旋转45度,且四个角都在一个格子当中。下图是四个可行的菱形,每个菱形和应该包含的格子都用了相应颜色标注在图中。</p> |
| 16 | +<img alt="" src="https://assets.leetcode.com/uploads/2021/04/23/pc73-q4-desc-2.png" style="width: 385px; height: 385px;" /> |
| 17 | +<p> </p> |
| 18 | + |
| 19 | +<p>注意,菱形可以是一个面积为 0 的区域,如上图中右下角的紫色菱形所示。</p> |
| 20 | + |
| 21 | +<p>请你按照 <strong>降序</strong> 返回 <code>grid</code> 中三个最大的 <strong>互不相同的菱形和</strong> 。如果不同的和少于三个,则将它们全部返回。</p> |
| 22 | + |
| 23 | +<p> </p> |
| 24 | + |
| 25 | +<p><strong>示例 1:</strong></p> |
| 26 | +<img alt="" src="https://assets.leetcode.com/uploads/2021/04/23/pc73-q4-ex1.png" style="width: 360px; height: 361px;" /> |
| 27 | +<pre> |
| 28 | +<b>输入:</b>grid = [[3,4,5,1,3],[3,3,4,2,3],[20,30,200,40,10],[1,5,5,4,1],[4,3,2,2,5]] |
| 29 | +<b>输出:</b>[228,216,211] |
| 30 | +<b>解释:</b>最大的三个菱形和如上图所示。 |
| 31 | +- 蓝色:20 + 3 + 200 + 5 = 228 |
| 32 | +- 红色:200 + 2 + 10 + 4 = 216 |
| 33 | +- 绿色:5 + 200 + 4 + 2 = 211 |
| 34 | +</pre> |
| 35 | + |
| 36 | +<p><strong>示例 2:</strong></p> |
| 37 | +<img alt="" src="https://assets.leetcode.com/uploads/2021/04/23/pc73-q4-ex2.png" style="width: 217px; height: 217px;" /> |
| 38 | +<pre> |
| 39 | +<b>输入:</b>grid = [[1,2,3],[4,5,6],[7,8,9]] |
| 40 | +<b>输出:</b>[20,9,8] |
| 41 | +<b>解释:</b>最大的三个菱形和如上图所示。 |
| 42 | +- 蓝色:4 + 2 + 6 + 8 = 20 |
| 43 | +- 红色:9 (右下角红色的面积为 0 的菱形) |
| 44 | +- 绿色:8 (下方中央面积为 0 的菱形) |
| 45 | +</pre> |
| 46 | + |
| 47 | +<p><strong>示例 3:</strong></p> |
| 48 | + |
| 49 | +<pre> |
| 50 | +<b>输入:</b>grid = [[7,7,7]] |
| 51 | +<b>输出:</b>[7] |
| 52 | +<b>解释:</b>所有三个可能的菱形和都相同,所以返回 [7] 。 |
| 53 | +</pre> |
| 54 | + |
| 55 | +<p> </p> |
| 56 | + |
| 57 | +<p><strong>提示:</strong></p> |
| 58 | + |
| 59 | +<ul> |
| 60 | + <li><code>m == grid.length</code></li> |
| 61 | + <li><code>n == grid[i].length</code></li> |
| 62 | + <li><code>1 <= m, n <= 100</code></li> |
| 63 | + <li><code>1 <= grid[i][j] <= 10<sup>5</sup></code></li> |
| 64 | +</ul> |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | +## 解题方法:斜向前缀和 |
| 69 | + |
| 70 | +严格按照以下约定计算和使用前缀和: |
| 71 | + |
| 72 | ++ ↘ `(i, j) -> (x, y)`: $diag[x+1][y+1] - diag[i][j]$ |
| 73 | ++ ↗ `(i, j) -> (x, y)`: $anti[i+1][j] - anti[x][y+1]$ |
| 74 | + |
| 75 | +遍历一遍原始数组我们即可得到前缀和数组。 |
| 76 | + |
| 77 | +然后枚举每个菱形的中心和菱形的二分之一对角线长度$k$,依据前缀和计算4条边的长度,三个变量维护三个最大的不同周长。 |
| 78 | + |
| 79 | ++ 时间复杂度$O(nm\min(m,n))$ |
| 80 | ++ 空间复杂度$O(mn)$ |
| 81 | + |
| 82 | +主要是很多边界值加一减一的要细心。 |
| 83 | + |
| 84 | +### AC代码 |
| 85 | + |
| 86 | +#### C++ |
| 87 | + |
| 88 | +```cpp |
| 89 | +/* |
| 90 | + * @LastEditTime: 2026-03-17 00:04:02 |
| 91 | + */ |
| 92 | +/* |
| 93 | +↘ (i, j) -> (x, y): diag[x+1][y+1] - diag[i][j] |
| 94 | +↗ (i, j) -> (x, y): anti[i+1][j] - anti[x][y+1] |
| 95 | +*/ |
| 96 | +class Solution { |
| 97 | +private: |
| 98 | + int x = 0, y = 0, z = 0; // 三大 |
| 99 | + vector<vector<int>> diag, anti; |
| 100 | + |
| 101 | + void update(int v) { |
| 102 | + if (v > x) { |
| 103 | + z = y, y = x, x = v; |
| 104 | + } else if (v < x && v > y) { |
| 105 | + z = y, y = v; |
| 106 | + } else if (v < y && v > z) { |
| 107 | + z = v; |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + void calc(int i, int j, int k) { |
| 112 | + if (!k) { |
| 113 | + return update(diag[i + 1][j + 1] - diag[i][j]); |
| 114 | + } |
| 115 | + // 上:i - k, j |
| 116 | + // 下:i + k, j |
| 117 | + // 左:i, j - k |
| 118 | + // 右:i, j + k |
| 119 | + int val = 0 |
| 120 | + + diag[i][j + k] - diag[i - k][j] // ↘ [上, 右):(i-k, j)->(i, j+k) | (i-k,j)->(i-1, j+k-1) |
| 121 | + + diag[i + k + 1][j + 1] - diag[i + 1][j - k + 1] // ↘ (左, 下]:(i,j-k)->(i+k,j) | (i+1,j-k+1)->(i+k,j) |
| 122 | + + anti[i + 1][j - k] - anti[i - k + 1][j] // ↗ [左, 上):(i,j-k)->(i-k,j) | (i,j-k)->(i-k+1,j-1) |
| 123 | + + anti[i + k][j + 1] - anti[i][j + k + 1]; // ↗ (下, 右]:(i+k,j)->(i,j+k) | (i+k-1,j+1)->(i,j+k) |
| 124 | + update(val); |
| 125 | + } |
| 126 | +public: |
| 127 | + vector<int> getBiggestThree(vector<vector<int>>& grid) { |
| 128 | + int n = grid.size(), m = grid[0].size(); |
| 129 | + diag = vector<vector<int>>(n + 1, vector<int>(m + 1)); // ↘ |
| 130 | + anti = vector<vector<int>>(n + 1, vector<int>(m + 1)); // ↖ |
| 131 | + for (int i = 0; i < n; i++) { |
| 132 | + for (int j = 0; j < m; j++) { |
| 133 | + diag[i + 1][j + 1] = diag[i][j] + grid[i][j]; |
| 134 | + anti[i + 1][j] = anti[i][j + 1] + grid[i][j]; |
| 135 | + } |
| 136 | + } |
| 137 | + |
| 138 | + for (int i = 0; i < n; i++) { |
| 139 | + for (int j = 0; j < m; j++) { |
| 140 | + for (int k = 0, max_k = min(i, min(j, min(n - i - 1, m - j - 1))); k <= max_k; k++) { |
| 141 | + calc(i, j, k); |
| 142 | + } |
| 143 | + } |
| 144 | + } |
| 145 | + |
| 146 | + vector<int> ans; |
| 147 | + if (x) { |
| 148 | + ans.push_back(x); |
| 149 | + } |
| 150 | + if (y) { |
| 151 | + ans.push_back(y); |
| 152 | + } |
| 153 | + if (z) { |
| 154 | + ans.push_back(z); |
| 155 | + } |
| 156 | + return ans; |
| 157 | + } |
| 158 | +}; |
| 159 | +``` |
| 160 | +
|
| 161 | +> 同步发文于[CSDN](https://letmefly.blog.csdn.net/article/details/159162029)和我的[个人博客](https://blog.letmefly.xyz/),原创不易,转载经作者同意后请附上[原文链接](https://blog.letmefly.xyz/2026/03/17/LeetCode%201878.%E7%9F%A9%E9%98%B5%E4%B8%AD%E6%9C%80%E5%A4%A7%E7%9A%84%E4%B8%89%E4%B8%AA%E8%8F%B1%E5%BD%A2%E5%92%8C/)哦~ |
| 162 | +> |
| 163 | +> 千篇源码题解[已开源](https://github.com/LetMeFly666/LeetCode) |
0 commit comments