题意
给出n*m的矩阵,可以横着切一刀或是竖着切一刀,问切完后两块和的差值的最小值
思路1:暴力枚举
首先能想到的是O(n^3)的暴力,以横着切一刀为例,枚举切的位置是i,那么1~i 行的元素之和就是第一块的值s1,i+1~n行的元素之和就是第二块的值s2,大概的代码如下
for i := 1; i <= n; i++ { //切的位置 var s1,s2 int64 = 0,0 for k := 1; k <= i; k ++ { for j := 1; j <= m; j ++ { s1 += int64(a[k][j]) } } for k := i+1; k <= n; k ++ { for j := 1; j <= m; j ++ { s2 += int64(a[k][j]) } } ans = min(ans,abs(s1-s2)) }
思路2:一维前缀和
思路1会有很多重复计算的地方,而且数组a是不变的数组,所以可以预处理下需要计算的信息,以减少时间复杂度。
还是以横着切为例,枚举当前切的位置为i,那么对于每个位置i来说,第一块的面积是前i行矩阵元素的和,第二块的面积是i+1行到n行矩阵元素的和,那么我们只需要用前缀和数组sum1[x]表示前x行矩阵元素的和就可以了。
竖着切也同理。
golang代码一直超时只能用cpp写了,要注意开long long
#include <iostream> #include <vector> #include <cmath> #include <algorithm> using namespace std; int main() { int n, m; cin >> n >> m; vector<vector<int>> a(n + 1, vector<int>(m + 1)); for (int i = 1; i <= n; ++i) { for (int j = 1; j <= m; ++j) { cin >> a[i][j]; } } vector<long long> sum1(n + 1, 0), sum2(m + 1, 0); for (int i = 1; i <= n; ++i) { int tmp = 0; for (int j = 1; j <= m; ++j) { tmp += a[i][j]; } sum1[i] = sum1[i - 1] + tmp; } for (int j = 1; j <= m; ++j) { int tmp = 0; for (int i = 1; i <= n; ++i) { tmp += a[i][j]; } sum2[j] = sum2[j - 1] + tmp; } long long ans = 1e9; for (int i = 1; i <= n; ++i) { long long s1 = sum1[i] - sum1[0]; long long s2 = sum1[n] - sum1[i]; ans = min(ans, abs(s1 - s2)); } for (int j = 1; j <= m; ++j) { long long s1 = sum2[j] - sum2[0]; long long s2 = sum2[m] - sum2[j]; ans = min(ans, abs(s1 - s2)); } cout << ans << endl; return 0; }
思路3:二维前缀和
核心点和思路2一样,不过换了种实现方法。
还是以横着切为例,如果当前切的位置是i的话,那么第一块的大小是sum[i][m] ,第二块的大小是sum[n][m]-sum[i][m],这样的话就是默认把左上角的节点看成矩形的左上角。
#include <iostream> #include <vector> #include <cmath> #include <algorithm> using namespace std; int main() { int n, m; cin >> n >> m; vector<vector<int>> a(n + 1, vector<int>(m + 1)); vector<vector<long long>> sum(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]; sum[i][j] = sum[i][j-1] + sum[i-1][j] - sum[i-1][j-1] + a[i][j]; } } long long ans = 1e9; // 枚举横着切还是竖着切 for (int i = 1; i <= n; ++i) { ans = min(ans, abs(sum[n][m] - sum[i][m] * 2)); } for (int j = 1; j <= m; ++j) { ans = min(ans, abs(sum[n][m] - sum[n][j] * 2)); } cout << ans << endl; return 0; }