问题描述
小M有一个 n×mn×m 的矩形蛋糕,蛋糕被分为 n×mn×m 个区域,每个区域都有一个美味度。她希望通过一刀将蛋糕切成两部分,一部分自己吃,另一部分给小团。切下的区域必须是完整的,即不能把某个小正方形切成两个小区域。
小M希望两个人吃的部分的美味度之和尽量接近。你的任务是计算出美味度差的最小值,即 ∣s1−s2∣∣s1−s2∣ 的最小值,其中 s1s1 是小M吃到的美味度,s2s2 是小团吃到的美味度。
测试样例
样例1:
输入:
n = 2, m = 3, a = [[1, 1, 4], [5, 1, 4]]
输出:0
样例2:
输入:
n = 3, m = 3, a = [[3, 2, 1], [4, 5, 6], [7, 8, 9]]
输出:3
样例3:
输入:
n = 2, m = 2, a = [[1, 2], [3, 4]]
输出:2
问题理解
需要将一个 n x m
的矩形蛋糕切成两部分,使得两部分的美味度之和尽量接近。美味度之和的差值需要最小化。
数据结构选择
- 二维数组:用于存储蛋糕的美味度。
- 前缀和数组:用于快速计算任意子矩阵的美味度之和。
算法步骤
- 计算前缀和:首先计算一个二维前缀和数组
prefixSum
,其中prefixSum[i][j]
表示从(0,0)
到(i,j)
的子矩阵的美味度之和。- 枚举切割线:
- 水平切割:枚举每一行作为切割线,计算上下两部分的美味度之和。
- 垂直切割:枚举每一列作为切割线,计算左右两部分的美味度之和。
- 计算差值:对于每一种切割方式,计算两部分的美味度之和的差值,并记录最小差值。
代码实现:
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int solution(int n, int m, vector<vector<int>> a) {
// 计算前缀和数组
vector<vector<int>> prefixSum(n + 1, vector<int>(m + 1, 0));
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 = INT_MAX;
// 枚举水平切割线
for (int i = 1; i < n; ++i) {
int sum1 = prefixSum[i][m];
int sum2 = prefixSum[n][m] - sum1;
minDiff = min(minDiff, abs(sum1 - sum2));
}
// 枚举垂直切割线
for (int j = 1; j < m; ++j) {
int sum1 = prefixSum[n][j];
int sum2 = prefixSum[n][m] - sum1;
minDiff = min(minDiff, abs(sum1 - sum2));
}
return minDiff;
}
int main() {
cout << (solution(2, 3, {{1, 1, 4}, {5, 1, 4}}) == 0) << endl;
cout << (solution(3, 3, {{3, 2, 1}, {4, 5, 6}, {7, 8, 9}}) == 3) << endl;
cout << (solution(2, 2, {{1, 2}, {3, 4}}) == 2) << endl;
return 0;
}