问题描述
小R手里有一个大小为 n
行 m
列的矩形蛋糕,每个小正方形区域都有一个代表美味度的整数。小R打算切割出一个正方形的小蛋糕给自己,而剩下的部分将给小S。她希望两人吃的部分的美味度之和尽量接近。
我们定义小R吃到的部分的美味度之和为 s_1
,而小S吃到的部分的美味度之和为 s_2
,请你帮助小R找到一个切割方案,使得 |s_1 - s_2|
的值最小。
问题理解
我们有一个 n
行 m
列的矩形蛋糕,每个小正方形区域都有一个代表美味度的整数。我们需要找到一个正方形的小蛋糕,使得小R和小S吃到的部分的美味度之和尽量接近。
数据结构选择
为了快速计算任意子矩阵的美味度之和,我们可以使用前缀和数组。前缀和数组可以帮助我们在常数时间内计算任意子矩阵的美味度之和。
算法步骤
-
计算前缀和数组:
- 创建一个
(n+1) x (m+1)
的前缀和数组prefixSum
。 - 遍历原始矩阵
a
,计算每个位置的前缀和。prefixSum[i][j]
表示从(0, 0)
到(i-1, j-1)
的子矩阵的美味度之和。
- 创建一个
-
枚举所有可能的正方形:
- 对于每个可能的正方形大小
size
(从 1 到Math.min(n, m)
),枚举所有可能的正方形的左上角坐标(i, j)
。 - 计算当前正方形的美味度之和
s1
。
- 对于每个可能的正方形大小
-
计算差值:
- 计算小S吃到的部分的美味度之和
s2
,即整个矩阵的美味度之和减去s1
。 - 计算
|s1 - s2|
,并记录最小的差值。
- 计算小S吃到的部分的美味度之和
复杂度分析
- 时间复杂度:计算前缀和数组的时间复杂度为
O(n * m)
,枚举所有可能的正方形的时间复杂度为O(n * m * min(n, m))
。因此,总的时间复杂度为O(n * m * min(n, m))
。 - 空间复杂度:使用了一个
(n+1) x (m+1)
的前缀和数组,因此空间复杂度为O(n * m)
。
总结
通过使用前缀和数组,我们可以在常数时间内计算任意子矩阵的美味度之和,从而高效地找到一个正方形的小蛋糕,使得小R和小S吃到的部分的美味度之和尽量接近。
solution完整代码
public static int solution(int n, int m, int[][] a) {
// 计算前缀和数组
int[][] prefixSum = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
prefixSum[i][j] = a[i - 1][j - 1] + prefixSum[i - 1][j] + prefixSum[i][j - 1] - prefixSum[i - 1][j - 1];
}
}
int minDiff = Integer.MAX_VALUE;
// 枚举所有可能的正方形
for (int size = 1; size <= Math.min(n, m); size++) {
for (int i = 0; i <= n - size; i++) {
for (int j = 0; j <= m - size; j++) {
int s1 = getSubMatrixSum(prefixSum, i, j, size);
int s2 = prefixSum[n][m] - s1;
int diff = Math.abs(s1 - s2);
minDiff = Math.min(minDiff, diff);
}
}
}
return minDiff;
}
结果分析
通过分析测试样例,我们可以看到代码能够正确地找到最优的正方形切割方案,使得小R和小S吃到的部分的美味度之和尽量接近。代码的输出结果与预期结果一致,说明代码实现是正确的。