问题描述
小R手里有一个大小为 n
行 m
列的矩形蛋糕,每个小正方形区域都有一个代表美味度的整数。小R打算切割出一个正方形的小蛋糕给自己,而剩下的部分将给小S。她希望两人吃的部分的美味度之和尽量接近。
我们定义小R吃到的部分的美味度之和为 s_1
,而小S吃到的部分的美味度之和为 s_2
,请你帮助小R找到一个切割方案,使得 |s_1 - s_2|
的值最小。
测试样例
样例1:
输入:
n = 3, m = 3, a = [[1, 2, 3], [2, 3, 4], [3, 2, 1]]
输出:1
样例2:
输入:
n = 4, m = 4, a = [[1, 2, 3, 4], [4, 3, 2, 1], [1, 2, 3, 4], [4, 3, 2, 1]]
输出:2
样例3:
输入:
n = 2, m = 2, a = [[5, 5], [5, 5]]
输出:10
Python实现
思路说明
题目要求在给定的 n
行 m
列的矩形蛋糕中,找到一个正方形的小蛋糕,使得小R和小S吃到的部分的美味度之和尽量接近。我们需要计算出所有可能的正方形的美味度之和,并找到使得 |s_1 - s_2|
最小的切割方案。为了高效地计算任意子矩阵的美味度之和,我们可以使用前缀和数组来预处理原始矩阵,从而在 O(1) 时间内计算出任意子矩阵的美味度之和。接着,我们通过二分查找来确定每个位置的最大正方形边长,使得美味度之和满足条件。
解题过程
-
前缀和数组构建:
- 我们构建一个
(n+1) x (m+1)
的前缀和数组s
,其中s[i][j]
表示从(0,0)
到(i-1,j-1)
的子矩阵的美味度之和。 - 通过动态规划的方式,计算出每个位置的前缀和:
s[i][j] = a[i-1][j-1] + s[i][j-1] + s[i-1][j] - s[i-1][j-1]
- 我们构建一个
-
二分查找确定最大正方形边长:
- 对于每个位置
(i, j)
,我们通过二分查找确定以该位置为右下角的正方形的最大边长l
,使得正方形的美味度之和cal(i, j, l)
满足cal(i, j, l) * 2 <= s[n][m]
。 - 二分查找的范围是从
0
到min(i, j)
。
- 对于每个位置
-
计算最小差值:
- 对于每个位置
(i, j)
,计算出满足条件的最大正方形边长l
,并计算出|s[n][m] - cal(i, j, l) * 2|
的值。 - 如果
l
不是最大边长,还需要检查l+1
的情况,以确保找到最优解。
- 对于每个位置
复杂度分析
-
时间复杂度:
- 构建前缀和数组的时间复杂度为 O(n×m)。
- 对于每个位置
(i, j)
,二分查找的时间复杂度为 O(log(min(i,j))),总共有 O(n×m) 个位置,因此总时间复杂度为 O(n×m×log(min(i,j)))。
-
空间复杂度:
- 前缀和数组的空间复杂度为 O(n×m)。
知识点扩展
-
前缀和:
- 前缀和是一种常用的预处理技术,用于快速计算数组中任意子数组的和。在本题中,我们通过构建二维前缀和数组,可以在 O(1) 时间内计算出任意子矩阵的美味度之和。
-
二分查找:
- 二分查找是一种高效的查找算法,适用于在有序数组中查找特定值。在本题中,我们通过二分查找来确定每个位置的最大正方形边长,从而避免了暴力枚举所有可能的正方形边长,提高了算法的效率。
代码实现
def solution(n:int,m:int,a:list)->int:
assert n == len(a) and all(m == len(v) for v in a)
s = [[0] * (m+1) for _ in range(n+1)]
def cal(x, y, l):
return s[x][y] - s[x - l][y] - s[x][y - l] + s[x - l][y - l]
for i in range(1, n + 1):
for j in range(1, m + 1):
s[i][j] = a[i-1][j-1]
s[i][j] += s[i][j - 1] + s[i - 1][j] - s[i - 1][j - 1]
ans = s[n][m]
for i in range(1, n + 1):
for j in range(1, m + 1):
k = min(i, j)
l, r = 0, k
while l < r:
mid = (l + r + 1) // 2
if cal(i, j, mid) * 2 <= s[n][m]:
l = mid
else:
r = mid - 1
ans = min(ans, abs(s[n][m] - cal(i, j, l) * 2))
if l < k:
ans = min(ans, abs(s[n][m] - cal(i, j, l + 1) * 2))
return ans
if __name__ == '__main__':
print(solution(n = 3, m = 3, a = [[1, 2, 3], [2, 3, 4], [3, 2, 1]]) == 1)
print(solution(n = 4, m = 4, a = [[1, 2, 3, 4], [4, 3, 2, 1], [1, 2, 3, 4], [4, 3, 2, 1]]) == 2)
print(solution(n = 2, m = 2, a = [[5, 5], [5, 5]]) == 10)