原题地址:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=44
题目翻译
通常一个在一维上很好解决的问题扩展到二维后就难解决多了。类似的还有2-SAT和3-SAT(NPC问题)。
给定一个二维数组,找出一个子矩阵最大化其个元素的和。
比如数组:
0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
最左下角的一个子矩阵:
9 2
-4 1
-1 8
的和15最大。
输入
第一行一个整数
N(N≤100)
,表示二维数组的尺寸。
接下来
N×N
个数,每个数字间可以用空格或换行隔开。数组元素
(−127≤ai,j≤127)
按从左到右,从上到下的顺序给出。
输出
一行一个整数,表示子矩阵的最大和。
样例输入
4
0 -2 -7 0 9 2 -6 2
-4 1 -4 1 -1
8 0 -2
样例输出
15
题解
题目给了很明显的暗示。
我们考虑一维问题,给定一个数列,求最大子串和。对于已经求出的前缀和
sumi
,一个子串和可以表示为
sumr−suml−1
,如果我们枚举r,只要知道一个最小的
suml−1
即可。这个我们在扫sum的时候就可以顺便求了。
扩展到二维的话,我们考虑枚举一维,另一维仍按照上面的做法做。
#include <cstdio>
#define FOR(i,j,k) for(i=j;i<=k;++i)
const int N = 105;
int mat[N][N], sum[N][N];
int main() {
int n, i, j, k;
scanf("%d", &n);
FOR(i,1,n) FOR(j,1,n) {
scanf("%d", &mat[i][j]);
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + mat[i][j];
}
int ans = 0x80000000;
FOR(i,1,n) FOR(j,1,i) { // j~i
int mi = 0;
FOR(k,1,n) {
int t = sum[i][k] - sum[j - 1][k] - mi;
if (t > ans) ans = t;
if (sum[i][k] - sum[j - 1][k] < mi)
mi = sum[i][k] - sum[j - 1][k];
}
}
printf("%d\n", ans);
return 0;
}