解题思路
看到题目后,我们很容易得出一个结论:令所有蛋糕的美味度总和为sum。那么当s1越接近sum的一半,|s1 - s2|就越小。
同时s1 + s2 = sum,故|s1 - s2| = |sum - s1 - s1|。
本题的关键是求计算s1,即枚举所有正方形的美味度。
将一维数组的前缀和扩展到二维
对于一维数组a,我们可以维护一个前缀和数组s。s[i]表示a[0]到a[i]的和。那么当我们要求数组a的一段连续子数组的和,比如a[i]到a[j]的和,就可以得到答案 = s[j] - s[i - 1]。
类似的,将一维数组扩展到二维数组。
s[i][j]表示从a[0][0]开始,a[i][j]结束的矩形范围的所有值之和【面积】。
那么我们求二维数组内的任意一块矩形的【面积】,比如从a[x1][y1],到a[x2][y2]。可以得到答案 =
s[x2][y2] - s[x1- 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]
上面这个式子可以看成一个大矩形的面积 - 左侧部分矩形面积 - 上侧部分矩形面积 + 左侧和上侧两个矩形重合部分面积
如下图红框矩形面积 = 大黑框面积 - 绿框面积 - 蓝框面积 + 蓝绿框重合面积。
贪心
遍历二维数组的每个位置,以该位置作为正方形的右下角顶点,可以得到边长为 l 的正方形面积
s1 = s[i][j] - s[i][j - l] - s[i - l][j] + s[i - l][j - l];
很显然边长 l 是一个可以变化的值,如果用一个for循环从大到小遍历一遍,那么就可以得到多个s1,从大到小变化。
根据贪心的思路,s1越接近总面积sum的一半越好。因此当s1小于sum / 2时,就不用计算边长更小的正方形了,因为那肯定不会是更优的解。
C++代码
#include <iostream> #include <vector> using namespace std; int main() { int m, n; cin >> n >> m; vector<vector<int>> a(n + 1, vector<int>(m + 1, 0)); vector<vector<long long>> s(n + 1, vector<long long>(m + 1, 0)); //前缀和 for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { cin >> a[i][j]; s[i][j] = s[i][j - 1] + a[i][j]; //当前行的前缀和 } for (int j = 1; j <= m; ++j) { s[i][j] += s[i - 1][j]; //扩展到面积 } } long long sum = s[n][m]; //总面积 long long half = sum / 2; //总面积的一半 long long ans = sum; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { for (int l = min(i, j); l >= 1; --l) { //l表示正方形边长 long long s1 = s[i][j] - s[i][j - l] - s[i - l][j] + s[i - l][j - l]; ans = min(ans, abs(sum - s1 - s1)); //更新结果 if (s1 <= half) break; //继续循环不可能得到更优结果 } } } cout << ans; } // 64 位输出请用 printf("%lld")