问题描述
小R手里有一个大小为 n 行 m 列的矩形蛋糕,每个小正方形区域都有一个代表美味度的整数。小R打算切割出一个正方形的小蛋糕给自己,而剩下的部分将给小S。她希望两人吃的部分的美味度之和尽量接近。
我们定义小R吃到的部分的美味度之和为 s_1,而小S吃到的部分的美味度之和为 s_2,请你帮助小R找到一个切割方案,使得 |s_1 - s_2| 的值最小。
解题思路
1. 问题分解
要解决问题,我们需要:
- 快速计算正方形区域的美味度之和:通过前缀和预处理,快速获得任意子矩形的美味度总和;
- 枚举所有可能的正方形切割方式:尝试每一种正方形切割,并计算剩余部分的美味度,找到最优解;
- 更新最小差值:逐步记录最优解。
根据这个思路,可以将问题拆分为以下几部分:
- 通过切割出一个正方形子矩形计算其美味度和 。
- 计算剩余部分的美味度和 。
- 找到使 最小的切割方案。
2. 前缀和优化
为了快速计算任意子矩形的美味度和,使用 前缀和数组,定义如下:
前缀和公式:
使用前缀和,可以快速计算任意矩形区域的美味度和。例如,子矩形的左上角为 ,右下角为 ,其总和为:
3. 枚举正方形切割
枚举所有可能的正方形切割方式:
-
正方形左上角坐标:枚举 ,其中 。
-
正方形边长 :枚举满足正方形范围不超出矩形边界的边长 。对每个左上角 ,最大边长为 。
-
计算子矩形美味度:利用前缀和公式计算正方形区域的美味度 ,剩余部分的美味度为 。
-
更新最小差值:记录 的最小值。
目标是找到使 最小的切割方案。
4. 实现细节
代码
def solution(n, m, a):
# 构造前缀和
prefix_sum = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, m + 1):
prefix_sum[i][j] = (
a[i - 1][j - 1] +
prefix_sum[i - 1][j] +
prefix_sum[i][j - 1] -
prefix_sum[i - 1][j - 1]
)
# 总美味度
total_sum = prefix_sum[n][m]
min_diff = float('inf') # 初始化最小差值
# 枚举所有正方形子矩形
for i in range(1, n + 1): # 左上角行号
for j in range(1, m + 1): # 左上角列号
for l in range(1, min(n - i + 1, m - j + 1) + 1): # 正方形边长
# 子矩形右下角
x2, y2 = i + l - 1, j + l - 1
# 计算子矩形的美味度和 s1
s1 = (
prefix_sum[x2][y2]
- prefix_sum[i - 1][y2]
- prefix_sum[x2][j - 1]
+ prefix_sum[i - 1][j - 1]
)
# 剩余部分的美味度和
s2 = total_sum - s1
# 更新最小差值
min_diff = min(min_diff, abs(s1 - s2))
return min_diff
复杂度分析
-
前缀和计算:
构造前缀和数组需要 时间。 -
枚举正方形切割:
枚举左上角位置 的复杂度为 。对于每个位置,正方形边长最多为 ,因此复杂度为: -
总复杂度:
-
空间复杂度:
前缀和数组需要 空间。
测试用例
示例 1
输入:
输出:1
示例 2
输入:
输出:2
示例 3
输入:
, ,
输出:10
总结
通过前缀和优化和暴力枚举正方形,问题得以高效解决。虽然复杂度较高,但在数据范围内可以运行。进一步优化可以考虑动态规划或限制搜索空间以减少枚举次数。