题意:给一个n*m的矩阵,你只能选择一个格子把这个格子的数换成p(也可以一个都不换),问最大子矩阵和最小可能是多少?
思路:
思路就是上面这个思路,这里简单讲一下怎么n^3求最大子矩阵和:枚举两行(或者两列),然后把每一列之和看做一个数字,这样二维就变成了一维,我们可以直接求最大子串和的方法。初始一个ret为0,然后从左往右加,如果ret<0,那么把ret初始化0,负数作为初始值肯定比重新开始小,然后找出ret的最大值就是最大子矩阵和。
代码:
#include<cstdio> #include<cstring> typedef long long ll; using namespace std; const int maxn = 150 + 10; const int MOD = 1e9 + 7; const int INF = 0x3f3f3f3f; int num[maxn][maxn], sum[maxn][maxn]; int up[maxn], down[maxn], left[maxn], right[maxn]; int n, m, p, xx1, yy1, xx2, yy2; int min(int x, int y){ return x < y? x : y; } int max(int x, int y){ return x > y? x : y; } int mat(int x1, int y1, int x2, int y2){ return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1]; } int main(){ while(~scanf("%d%d%d", &n, &m, &p)){ memset(sum, 0, sizeof(sum)); for(int i = 1; i <= n; i++){ for(int j = 1; j <= m; j++){ scanf("%d", &num[i][j]); sum[i][j] = num[i][j] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1]; } } //上下左右四个矩阵预处理 memset(up, -INF ,sizeof(up)); for(int i = 1; i <= n; i++){ int tmp = -INF; for(int j = 1; j <= i; j++){ int ret = 0; for(int k = 1; k <= m; k++){ ret += mat(j, k, i, k); tmp = max(tmp, ret); if(ret < 0) ret = 0; } } up[i] = max(up[i - 1], tmp); } memset(down, -INF, sizeof(down)); for(int i = n; i >= 1; i--){ int tmp = -INF; for(int j = i; j <= n; j++){ int ret = 0; for(int k = 1; k <= m; k++){ ret += mat(i, k, j, k); tmp = max(tmp, ret); if(ret < 0) ret = 0; } } down[i] = max(down[i + 1], tmp); } memset(left, -INF, sizeof(left)); for(int i = 1; i <= m; i++){ int tmp = -INF; for(int j = 1; j <= i; j++){ int ret = 0; for(int k = 1; k <= n; k++){ ret += mat(k, j, k, i); tmp = max(ret, tmp); if(ret < 0) ret = 0; } } left[i] = max(left[i - 1], tmp); } memset(right, -INF, sizeof(right)); for(int i = m; i >= 1; i--){ int tmp = -INF; for(int j = i; j <= m; j++){ int ret = 0; for(int k = 1; k <= n; k++){ ret += mat(k, i, k, j); tmp = max(ret, tmp); if(ret < 0) ret = 0; } } right[i] = max(right[i + 1], tmp); } int Max = -INF; for(int i = 1; i <= n; i++){ for(int j = 1; j <= i; j++){ int ret = 0, start = 1;; for(int k = 1; k <= m; k++){ ret += mat(j, k, i, k); if(ret > Max){ Max = ret; xx1 = j, yy1 = start, xx2 = i, yy2 = k; } if(ret < 0) ret = 0, start = k + 1; } } } int ans = Max; for(int i = xx1; i <= xx2; i++){ for(int j = yy1; j <= yy2; j++){ if(p > num[i][j]) continue; ans = min(ans , max(Max - num[i][j] + p, max(up[i - 1], max(down[i + 1], max(left[j - 1], right[j + 1]))))); } } printf("%d ", ans); } return 0; }