问题描述
小M有一个 n×m 的矩形蛋糕,蛋糕被分为 n×m 个区域,每个区域都有一个美味度。她希望通过一刀将蛋糕切成两部分,一部分自己吃,另一部分给小团。切下的区域必须是完整的,即不能把某个小正方形切成两个小区域。
小M希望两个人吃的部分的美味度之和尽量接近。你的任务是计算出美味度差的最小值,即∣s1-s2∣的最小值,其中s1是小M吃到的美味度,s2是小团吃到的美味度。
解题思路
- 前缀和数组:在此题中,使用前缀和数组可以快速计算任意子矩阵的美味度。前缀和数组可以在常数时间内计算任意子矩阵的美味度。
- 枚举所有可能的切割方式:运用枚举法,枚举出所有可能的切割方式,包括水平切割和垂直切割。
- 计算美味度差:对于每一种切割方式,计算两部分的美味度差,并更新最小值。
代码实现
import java.util.Arrays;
public class Main {
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 i = 1; i < n; i++) {
int s1 = getSubmatrixSum(prefixSum, 0, 0, i, m);
int s2 = getSubmatrixSum(prefixSum, i, 0, n, m);
minDiff = Math.min(minDiff, Math.abs(s1 - s2));
}
// 枚举所有可能的垂直切割
for (int j = 1; j < m; j++) {
int s1 = getSubmatrixSum(prefixSum, 0, 0, n, j);
int s2 = getSubmatrixSum(prefixSum, 0, j, n, m);
minDiff = Math.min(minDiff, Math.abs(s1 - s2));
}
return minDiff;
}
// 计算子矩阵的美味度
private static int getSubmatrixSum(int[][] prefixSum, int x1, int y1, int x2, int y2) {
return prefixSum[x2][y2] - prefixSum[x1][y2] - prefixSum[x2][y1] + prefixSum[x1][y1];
}
public static void main(String[] args) {
System.out.println(solution(2, 3, new int[][]{{1, 1, 4}, {5, 1, 4}}) == 0);
System.out.println(solution(3, 3, new int[][]{{3, 2, 1}, {4, 5, 6}, {7, 8, 9}}) == 3);
System.out.println(solution(2, 2, new int[][]{{1, 2}, {3, 4}}) == 2);
}
}
代码说明
-
前缀和数组:
prefixSum[i][j]表示从(0, 0)到(i, j)的子矩阵的美味度之和。 -
计算前缀和:通过遍历原始矩阵
a,我们可以逐步构建前缀和数组。具体来说,对于每个位置(i, j),可以通过以下公式计算前缀和:prefixSum[i][j] = a[i-1][j-1] + prefixSum[i-1][j] + prefixSum[i][j-1] - prefixSum[i-1][j-1]
这里,
a[i-1][j-1]是当前位置的美味度,prefixSum[i-1][j]是上方子矩阵的美味度,prefixSum[i][j-1]是左方子矩阵的美味度,prefixSum[i-1][j-1]是左上角子矩阵的美味度。 -
计算总美味度:矩形蛋糕的总美味度就是
prefixSum[n][m],其中n和m分别是矩阵的行数和列数。
复杂度分析
1. 时间复杂度 :在这个问题中,前缀和数组的构建和查询操作的时间复杂度都是 O(1),因此整个算法的时间复杂度是 O(n * m),其中 n 和 m 分别是矩阵的行数和列数。
2. 空间复杂度 :在这个问题中,使用了一个额外的二维数组来存储前缀和,因此空间复杂度是 O(n * m)。
总结
通过对此题的练习,我对如何运用前缀和有了更深的了解,通过构建前缀和数组,可以在常数时间内计算任意子区间的和。同时我还学到了如何处理边界条件,在构建前缀和数组时确保数组的索引不会越界。并且对如何访问和操作二维数组的元素也更加熟练。