相机对焦区域最优选择(2026.11.04)
2026年最新华为OD机考题库(按考点分类): 2026年最新华为OD机考题库
题目描述
在收集相机应用的自动对焦系统中,画面被划分为 m×n 个对焦区域,每个区域的对比度用一个整数表示。现在需要选择一个面积恰好为 maxArea 的矩形区域,使其平均对比度值最大。
定义:选取一个 h 行 × w 列的连续矩形区域 R(h×w=maxArea,且矩形须完全位于矩阵内),其平均对比度为
avg(R)=maxArea∑(i,j)∈RcontrastMatrix[i][j]
其中求和范围为区域 R 内的所有格子 (i,j)。由于面积固定为 maxArea,分母是常数,因此
「平均对比度值最大」等价于「矩形区域内对比度求和最大」。
请返回所有合法矩形中 avg(R) 的最大值,保留 5 位小数。
说明
- 答案的 5 位小数形式是唯一的,评测按逐字节一致比对,请严格按输出描述的格式输出。
输入描述
输入只有一行,形如 [[1,2,3],[4,5,6]],2:先用方括号包住整个矩阵,行与行之间用 ],[ 分隔,同一行元素用半角逗号分隔;矩阵之后紧跟一个半角逗号,再给出整数 maxArea。m 为矩阵行数,n 为每行元素个数。读入应容忍逗号/方括号前后的多余空白。
输出描述
输出一行,为最大平均对比度,格式固定为整数部分 + 小数点 + 恰好 5 位小数,第 6 位小数四舍五入,位数不足补 0。评测按逐字节一致比对。
样例
用例输入 1
[[1,2,3],[4,5,6]],2
用例输出 1
5.50000
用例解释 1
面积必须恰好为 2,可行的尺寸只有 1×2 与 2×1:
- 选第 2 行的
[5,6],平均值 =(5+6)/2=5.5; - 或
[2,3],平均值 =2.5; - 或
[4,5],平均值 =4.5; - 2×1 的最佳是第 3 列的
(3,6),和 =9,平均值 =4.5。
最大为 5.5,按格式返回 5.50000。
用例输入 2
[[10,20],[30,40]],1
用例输出 2
40.00000
用例解释 2
maxArea = 1,只能选单个格子,最大值是 40,按格式返回 40.00000。
用例输入 3
[[100,50,200],[150,80,120]],3
用例输出 3
116.66667
用例解释 3
面积恰好为 3,可选 1×3 或 3×1。矩阵是 2 行 3 列,3×1 需要 3 行,放不下,所以只能选 1×3:
- 第 1 行
[100,50,200]:350/3≈116.66667; - 第 2 行
[150,80,120]:350/3≈116.66667。
最大为 116.66667。
来源
本题来源于网络、非官方材料、仅供学习交流,侵删
🧠 解题思路
第一步:把“平均值最大”换成“和最大”
平均对比度 = 矩形里所有数的和 ÷ maxArea。所有候选矩形的面积都是 maxArea,分母一样,所以谁的和大,谁的平均值就大。问题变成:在所有面积为 maxArea 的矩形里,找元素和最大的那个,最后再除以 maxArea。
第二步:矩形可能是哪些形状
设矩形高 h 行、宽 w 列,h × w = maxArea。让 h 从 1 试到 m(矩阵行数):
- h 必须能整除 maxArea,否则宽不是整数;
- 宽 w = maxArea / h 不能超过 n(矩阵列数),否则放不下。
例如 maxArea = 6、矩阵 4 行 5 列:h = 1 时 w = 6 > 5,放不下;h = 2 时 w = 3 ✓;h = 3 时 w = 2 ✓;h = 4 时 6 不能被 4 整除。只有 2×3 和 3×2 两种形状。横着放和竖着放会在不同的 h 上分别枚举到,不用另外处理。
第三步:最直接的做法为什么慢
形状定了以后,把矩形在矩阵里一格一格地挪,每到一个位置就把里面的 maxArea 个数加起来。矩阵最大 350×350,如果 maxArea = 10000(100×100),光这一种形状就有 251×251 ≈ 6.3 万个位置,每个位置要加 1 万个数,约 6×10⁸ 次加法,再算上其他形状就会超时。
慢在哪?相邻两个位置的矩形大部分是重叠的,重叠的部分被反复加了很多遍。前缀和就是用来消灭这种重复加法的。
第四步:前缀和是什么(先看一维)
数组 a = [3, 1, 4, 1, 5],想多次求“第 l 个到第 r 个数的和”。先算一个前缀和数组 pre,pre[k] 表示前 k 个数的和:
| k | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| pre[k] | 0 | 3 | 4 | 8 | 9 | 14 |
pre[0] = 0 表示一个数都不取;之后 pre[k] = pre[k − 1] + a 的第 k 个数,从左到右扫一遍就算完。
第 2 到第 4 个数(1、4、1)的和 = 前 4 个数的和 − 前 1 个数的和 = pre[4] − pre[1] = 9 − 3 = 6。
原理:前 4 个 = 前 1 个 + 第 2~4 个,两边相减,剩下的正好是要的那一段。不管这一段有多长,都只做一次减法。
第五步:推广到二维
定义 pre[i][j] = 矩阵前 i 行、前 j 列这个左上角区域里所有数的和。pre 比矩阵多一行一列,第 0 行、第 0 列全是 0,表示空区域。
怎么算 pre:左上角 i×j 区域 = 上面的 (i−1)×j 区域 + 左边的 i×(j−1) 区域 + 右下角那一格。上面和左边两块有一块公共部分 (i−1)×(j−1) 被加了两次,要减掉一次:
pre[i][j] = pre[i−1][j] + pre[i][j−1] − pre[i−1][j−1] + 矩阵第 i 行第 j 列的数(行列都从 1 数)
怎么用 pre 求任意矩形的和:设矩形右下角在第 i 行第 j 列,高 h、宽 w。
- 先取从矩阵左上角到这个右下角的整块:pre[i][j];
- 减去矩形上方那一块:pre[i−h][j];
- 减去矩形左边那一块:pre[i][j−w];
- 左上角那一小块 pre[i−h][j−w] 在第 2、3 步被减了两次,加回来一次。
矩形和 = pre[i][j] − pre[i−h][j] − pre[i][j−w] + pre[i−h][j−w]
这样每个位置只做 4 次加减,和 maxArea 多大无关了。
第六步:用样例 1 走一遍
矩阵 [[1,2,3],[4,5,6]],maxArea = 2,m = 2,n = 3。
① 算前缀和 pre(3 行 4 列):
| j=0 | j=1 | j=2 | j=3 | |
|---|---|---|---|---|
| i=0 | 0 | 0 | 0 | 0 |
| i=1 | 0 | 1 | 3 | 6 |
| i=2 | 0 | 5 | 12 | 21 |
例如 pre[2][2] = pre[1][2] + pre[2][1] − pre[1][1] + 5 = 3 + 5 − 1 + 5 = 12,正好是 1 + 2 + 4 + 5。
② 找形状:h = 1 时 w = 2 ✓;h = 2 时 w = 1 ✓。
③ 每个位置求和:
| 形状 | 右下角 (i, j) | 计算 | 和 | 对应的格子 |
|---|---|---|---|---|
| 1×2 | (1, 2) | 3 − 0 − 0 + 0 | 3 | 1、2 |
| 1×2 | (1, 3) | 6 − 0 − 1 + 0 | 5 | 2、3 |
| 1×2 | (2, 2) | 12 − 3 − 0 + 0 | 9 | 4、5 |
| 1×2 | (2, 3) | 21 − 6 − 5 + 1 | 11 | 5、6 |
| 2×1 | (2, 1) | 5 − 0 − 0 + 0 | 5 | 1、4 |
| 2×1 | (2, 2) | 12 − 0 − 5 + 0 | 7 | 2、5 |
| 2×1 | (2, 3) | 21 − 0 − 12 + 0 | 9 | 3、6 |
最大和 best = 11,平均值 11 / 2 = 5.5。
第七步:保留 5 位小数,为什么不用浮点格式化
直觉写法是 printf(“%.5f”, best / maxArea),但第 6 位小数正好是 5 时会出错,原因有两个:
- 1/64 = 0.015625 在浮点数里能精确存下,可格式化函数遇到“正好一半”时舍入到偶数,C++ 的 printf 和 Python 的 ‘%.5f’ 都输出 0.01562,题目要 0.01563;
- 3/320 = 0.009375 在浮点数里实际存成 0.0093749999…,第 6 位不到 5,输出 0.00937,题目要 0.00938。
所以改用整数做四舍五入:
- 要的是 best / maxArea × 100000 四舍五入后的整数 q;
- 四舍五入 = 加 0.5 再向下取整,通分后:q = ⌊(best × 200000 + maxArea) / (2 × maxArea)⌋;
- 整数部分是 q / 100000,小数部分是 q % 100000,不足 5 位左边补 0。
样例 1:q = (11 × 200000 + 2) / 4 = 2200002 / 4,向下取整得 550000,整数部分 5,小数部分 50000,输出 5.50000。
⚠️ 易错点
- 形状要同时满足两个条件:h 能整除 maxArea,并且 w = maxArea / h 不超过 n。
- 不能只试 1 × maxArea 或接近正方形的某一种形状,所有放得下的形状都要试。
- 二维前缀和的四项不要写错:减去“上方”和“左边”,加回“左上角”。
- 不能用浮点格式化输出:0.015625 应输出 0.01563,0.009375 应输出 0.00938,要用整数四舍五入。
- best 最大 255 × 350 × 350 = 31237500,int 放得下,但乘 200000 之前要转成 long / long long。
- 小数部分不足 5 位要左边补 0,例如 0.01563 的小数部分是 1563,要输出成 01563。
⏱️ 复杂度
时间复杂度:O(m·n·d),d 是放得下的形状个数。建前缀和要 m·n 次;每种形状最多 m·n 个位置,每个位置 4 次加减。最坏是 maxArea = 840:350×350 里有 28 种形状放得下,一共约 2.3×10⁶ 个位置,远在时限内。
空间复杂度:O(m·n),前缀和数组有 (m+1)×(n+1) 个数。
💻 参考代码(Java)
class Solution {
public String maxAverageContrast(int[][] contrastMatrix, int maxArea) {
int m = contrastMatrix.length, n = contrastMatrix[0].length;
// 二维前缀和:pre[i][j] 是前 i 行、前 j 列的元素和
int[][] pre = new int[m + 1][n + 1];
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
pre[i + 1][j + 1] = pre[i][j + 1] + pre[i + 1][j] - pre[i][j] + contrastMatrix[i][j];
int best = 0;
for (int h = 1; h <= m; h++) { // 枚举矩形高度 h,宽度 w = maxArea / h
if (maxArea % h != 0 || maxArea / h > n) continue;
int w = maxArea / h;
for (int i = h; i <= m; i++) // (i, j) 是矩形右下角在前缀和里的坐标
for (int j = w; j <= n; j++)
best = Math.max(best, pre[i][j] - pre[i - h][j] - pre[i][j - w] + pre[i - h][j - w]);
}
// 整数四舍五入到 5 位小数:q = round(best / maxArea × 10^5)
long q = (best * 200000L + maxArea) / (2L * maxArea);
return String.format("%d.%05d", q / 100000, q % 100000);
}
}
💻 参考代码(C++)
class Solution {
public:
string maxAverageContrast(vector<vector<int>>& contrastMatrix, int maxArea) {
int m = contrastMatrix.size(), n = contrastMatrix[0].size();
// 二维前缀和:pre[i][j] 是前 i 行、前 j 列的元素和
vector<vector<int>> pre(m + 1, vector<int>(n + 1, 0));
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
pre[i + 1][j + 1] = pre[i][j + 1] + pre[i + 1][j] - pre[i][j] + contrastMatrix[i][j];
int best = 0;
for (int h = 1; h <= m; h++) { // 枚举矩形高度 h,宽度 w = maxArea / h
if (maxArea % h != 0 || maxArea / h > n) continue;
int w = maxArea / h;
for (int i = h; i <= m; i++) // (i, j) 是矩形右下角在前缀和里的坐标
for (int j = w; j <= n; j++)
best = max(best, pre[i][j] - pre[i - h][j] - pre[i][j - w] + pre[i - h][j - w]);
}
// 整数四舍五入到 5 位小数:q = round(best / maxArea × 10^5)
long long q = (best * 200000LL + maxArea) / (2LL * maxArea);
char buf[32];
snprintf(buf, sizeof(buf), "%lld.%05lld", q / 100000, q % 100000);
return buf;
}
};
💻 参考代码(Python)
class Solution:
def maxAverageContrast(self, contrastMatrix, maxArea):
m, n = len(contrastMatrix), len(contrastMatrix[0])
# 二维前缀和:pre[i][j] 是前 i 行、前 j 列的元素和
pre = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m):
for j in range(n):
pre[i + 1][j + 1] = pre[i][j + 1] + pre[i + 1][j] - pre[i][j] + contrastMatrix[i][j]
best = 0
for h in range(1, m + 1): # 枚举矩形高度 h,宽度 w = maxArea / h
if maxArea % h or maxArea // h > n:
continue
w = maxArea // h
for i in range(h, m + 1): # (i, j) 是矩形右下角在前缀和里的坐标
for j in range(w, n + 1):
best = max(best, pre[i][j] - pre[i - h][j] - pre[i][j - w] + pre[i - h][j - w])
# 整数四舍五入到 5 位小数:q = round(best / maxArea × 10^5)
q = (best * 200000 + maxArea) // (2 * maxArea)
return '%d.%05d' % (q // 100000, q % 100000)
💻 参考代码(JavaScript)
class Solution {
maxAverageContrast(contrastMatrix, maxArea) {
const m = contrastMatrix.length, n = contrastMatrix[0].length;
// 二维前缀和:pre[i][j] 是前 i 行、前 j 列的元素和
const pre = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
for (let i = 0; i < m; i++)
for (let j = 0; j < n; j++)
pre[i + 1][j + 1] = pre[i][j + 1] + pre[i + 1][j] - pre[i][j] + contrastMatrix[i][j];
let best = 0;
for (let h = 1; h <= m; h++) { // 枚举矩形高度 h,宽度 w = maxArea / h
if (maxArea % h !== 0 || maxArea / h > n) continue;
const w = maxArea / h;
for (let i = h; i <= m; i++) // (i, j) 是矩形右下角在前缀和里的坐标
for (let j = w; j <= n; j++)
best = Math.max(best, pre[i][j] - pre[i - h][j] - pre[i][j - w] + pre[i - h][j - w]);
}
// 整数四舍五入到 5 位小数:q = round(best / maxArea × 10^5)
const q = Math.floor((best * 200000 + maxArea) / (2 * maxArea));
return Math.floor(q / 100000) + "." + String(q % 100000).padStart(5, "0");
}
}
module.exports = Solution;
4413



被折叠的 条评论
为什么被折叠?



