[POJ 1050]To the Max
给出一个数字矩阵,求子矩阵数字之和的最大值.这是最大子段和问题.(该题的AC代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> using namespace std; int n, a; long long sum, ans = -100000000; int main(){ cin >> n; for(int i = 0; i < n; i++){ cin >> a; sum += a; ans = max(ans, sum); if(sum < 0) sum = 0; } cout << ans << endl; return 0; }
,注意输入全为负数的情况)
上述一维情况本质上是在枚举和剪枝,可以通过简单的单层循环实现,发现和为负数时舍弃当前子段.
而二维情况下,没有办法这样"线性"枚举,想要舍弃时也有所顾虑.
由于数据比较小(n <= 100),直接暴力枚举子矩阵的左上角坐标(x1,y1)和右下角坐标(y1,y2)求解.O(n4)(粗略的)也不会爆.因为右下角坐标总是在左上角坐标之右下.
如何计算子矩阵数字之和?不用前缀和的话复杂度会变成O(n6).
从坐标(x1+1,y1+1)到(x2,y2)的所有数字之和即为:
s[x2][y2] + s[x1][y1] - s[x2][y1] - s[x1][y2]
由于矩阵是按从左上到右下的顺序给出的,前缀和在读入时就可以计算了.
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> using namespace std; int n, s[110][110], ans = -100000000; // 同样要考虑全为负数情况 int main(){ cin >> n; for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++){ cin >> s[i][j]; s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1]; } for(int x1 = 0; x1 <= n - 1; x1++) for(int y1 = 0; y1 <= n - 1; y1++) for(int x2 = x1 + 1; x2 <= n; x2++) for(int y2 = y1 + 1; y2 <= n; y2++) ans = max(ans, s[x2][y2] + s[x1][y1] - s[x2][y1] - s[x1][y2]); cout << ans << endl; return 0; }