问题描述
小M有一个 的矩形蛋糕,蛋糕被分为 个区域,每个区域都有一个美味度。她希望通过一刀将蛋糕切成两部分,一部分自己吃,另一部分给小团。切下的区域必须是完整的,即不能把某个小正方形切成两个小区域。
小M希望两个人吃的部分的美味度之和尽量接近。你的任务是计算出美味度差的最小值,即 的最小值,其中 是小M吃到的美味度, 是小团吃到的美味度。
题目分析
我们需要将一个 的矩形蛋糕用一刀切成两部分,使得两部分的美味度之和尽可能接近。具体来说,要计算两部分美味度的差值 的最小值,其中 是一部分美味度, 是另一部分美味度。
切割规则:
- 只能通过横向或纵向切割蛋糕。
- 切割必须经过整行或整列,不能把某个区域分成碎片。
目标: 找到最小的 。
解题思路
核心思想:
在横向和纵向切割中,逐一尝试每一种切割方式,计算两部分美味度的差值并记录最小值。
步骤1:前缀和的构造
为了快速计算任意区域的美味度,我们使用前缀和:
-
行前缀和: 每一行的前缀和数组存储该行从左到当前位置的美味度总和。
- 公式:
- 效果:快速获取第 行从第1列到第 列的美味度。
- 公式:
-
列前缀和: 每一列的前缀和数组存储该列从上到当前位置的美味度总和。
- 公式:
- 效果:快速获取第 列从第1行到第 行的美味度。
- 公式:
步骤2:尝试横向切割
对于横向切割,在每一行之间切割,切割后的两部分分别为:
- 上半部分:前 行的美味度总和。
- 下半部分:剩余行的美味度总和。
通过总美味度和上半部分美味度,可以快速计算下半部分美味度:
记录美味度差值的绝对值 ,更新最小值。
步骤3:尝试纵向切割
对于纵向切割,在每一列之间切割,切割后的两部分分别为:
- 左半部分:前 列的美味度总和。
- 右半部分:剩余列的美味度总和。
通过总美味度和左半部分美味度,可以快速计算右半部分美味度:
记录美味度差值的绝对值 ,更新最小值。
步骤4:输出结果
在横向和纵向切割中找到的最小差值即为答案。
代码实现
def solution(n: int, m: int, a: list) -> int:
# 计算行前缀和和列前缀和
row_prefix = [[0] * (m + 1) for _ in range(n)]
col_prefix = [[0] * (n + 1) for _ in range(m)]
# 填充前缀和
for i in range(n):
for j in range(m):
row_prefix[i][j + 1] = row_prefix[i][j] + a[i][j]
col_prefix[j][i + 1] = col_prefix[j][i] + a[i][j]
# 蛋糕总美味度
total_sum = sum(row_prefix[i][m] for i in range(n))
# 初始化最小差值
min_diff = float('inf')
# 横向切割
for i in range(1, n): # 从第1行开始尝试切割
top_sum = sum(row_prefix[k][m] for k in range(i)) # 上半部分美味度
bottom_sum = total_sum - top_sum # 下半部分美味度
min_diff = min(min_diff, abs(top_sum - bottom_sum))
# 纵向切割
for j in range(1, m): # 从第1列开始尝试切割
left_sum = sum(col_prefix[k][n] for k in range(j)) # 左半部分美味度
right_sum = total_sum - left_sum # 右半部分美味度
min_diff = min(min_diff, abs(left_sum - right_sum))
return min_diff
if __name__ == '__main__':
assert solution(2, 3, [[1, 1, 4], [5, 1, 4]]) == 0
assert solution(3, 3, [[3, 2, 1], [4, 5, 6], [7, 8, 9]]) == 3
assert solution(2, 2, [[1, 2], [3, 4]]) == 2
时间复杂度分析
-
前缀和计算:
- 遍历 的矩阵,时间复杂度为 。
-
横向切割:
- 遍历所有行,计算累积和,时间复杂度为 。
-
纵向切割:
- 遍历所有列,计算累积和,时间复杂度为 。
总时间复杂度为:
空间复杂度分析
-
使用了两个辅助数组存储前缀和,空间复杂度为:
- 行前缀和:
- 列前缀和:
空间复杂度为 。
测试样例解析
样例1:
输入:
n = 2, m = 3, a = [[1, 1, 4], [5, 1, 4]]
总美味度:。
最佳切割位置在第二列和第三列之间,左右部分美味度分别为 和 。
输出:。
样例2:
输入:
n = 3, m = 3, a = [[3, 2, 1], [4, 5, 6], [7, 8, 9]]
总美味度:。
最佳切割位置在第二行和第三行之间,上下部分美味度分别为 和 。
输出:。
样例3:
输入:
n = 2, m = 2, a = [[1, 2], [3, 4]]
总美味度:。
最佳切割位置在第一列和第二列之间,左右部分美味度分别为 和 。
输出:。
总结
该方法使用前缀和优化了美味度的计算,遍历所有可能的切割位置并找到最优解。